hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit c9fb75f584944c9e4a258fa1c2cffe2f3991681d
parent 6e12e7dcc81f80a405fa5018b7ff572141cc4c61
Author: Sebastian <sebastian@sebsite.pw>
Date:   Tue, 18 Jul 2023 18:56:08 -0400

math::checked: initial commit

Signed-off-by: Sebastian <sebastian@sebsite.pw>

Diffstat:
Amath/checked/README | 2++
Amath/checked/checked.ha | 377+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscripts/gen-stdlib | 6++++++
Mstdlib.mk | 34++++++++++++++++++++++++++++++++++
4 files changed, 419 insertions(+), 0 deletions(-)

diff --git a/math/checked/README b/math/checked/README @@ -0,0 +1,2 @@ +math::checked provides safe integer arithmetic functions that check for +overflow. diff --git a/math/checked/checked.ha b/math/checked/checked.ha @@ -0,0 +1,377 @@ +use math; + +// Adds 'a' and 'b', returning the result and whether overflow occurred. +export fn addi8(a: i8, b: i8) (i8, bool) = { + const res = a + b; + const overflow = a < 0 == b < 0 && a < 0 != res < 0; + return (res, overflow); +}; + +@test fn addi8() void = { + const (res, overflow) = addi8(100, 20); + assert(res == 120); + assert(!overflow); + const (res, overflow) = addi8(100, 50); + assert(res == -106); + assert(overflow); +}; + +// Adds 'a' and 'b', returning the result and whether overflow occurred. +export fn addi16(a: i16, b: i16) (i16, bool) = { + const res = a + b; + const overflow = a < 0 == b < 0 && a < 0 != res < 0; + return (res, overflow); +}; + +@test fn addi16() void = { + const (res, overflow) = addi16(32700, 60); + assert(res == 32760); + assert(!overflow); + const (res, overflow) = addi16(32700, 100); + assert(res == -32736); + assert(overflow); +}; + +// Adds 'a' and 'b', returning the result and whether overflow occurred. +export fn addi32(a: i32, b: i32) (i32, bool) = { + const res = a + b; + const overflow = a < 0 == b < 0 && a < 0 != res < 0; + return (res, overflow); +}; + +@test fn addi32() void = { + const (res, overflow) = addi32(2147483600, 40); + assert(res == 2147483640); + assert(!overflow); + const (res, overflow) = addi32(2147483600, 100); + assert(res == -2147483596); + assert(overflow); +}; + +// Adds 'a' and 'b', returning the result and whether overflow occurred. +export fn addi64(a: i64, b: i64) (i64, bool) = { + const res = a + b; + const overflow = a < 0 == b < 0 && a < 0 != res < 0; + return (res, overflow); +}; + +@test fn addi64() void = { + const (res, overflow) = addi64(9223372036854775800, 5); + assert(res == 9223372036854775805); + assert(!overflow); + const (res, overflow) = addi64(9223372036854775800, 10); + assert(res == -9223372036854775806); + assert(overflow); +}; + +// Adds 'a' and 'b', returning the result and whether overflow occurred. +export fn addu8(a: u8, b: u8) (u8, bool) = { + const res = a + b; + const overflow = res < a; + return (res, overflow); +}; + +@test fn addu8() void = { + const (res, overflow) = addu8(200, 50); + assert(res == 250); + assert(!overflow); + const (res, overflow) = addu8(200, 100); + assert(res == 44); + assert(overflow); +}; + +// Adds 'a' and 'b', returning the result and whether overflow occurred. +export fn addu16(a: u16, b: u16) (u16, bool) = { + const res = a + b; + const overflow = res < a; + return (res, overflow); +}; + +@test fn addu16() void = { + const (res, overflow) = addu16(65500, 30); + assert(res == 65530); + assert(!overflow); + const (res, overflow) = addu16(65500, 50); + assert(res == 14); + assert(overflow); +}; + +// Adds 'a' and 'b', returning the result and whether overflow occurred. +export fn addu32(a: u32, b: u32) (u32, bool) = { + const res = a + b; + const overflow = res < a; + return (res, overflow); +}; + +@test fn addu32() void = { + const (res, overflow) = addu32(4294967200, 90); + assert(res == 4294967290); + assert(!overflow); + const (res, overflow) = addu32(4294967200, 100); + assert(res == 4); + assert(overflow); +}; + +// Adds 'a' and 'b', returning the result and whether overflow occurred. +export fn addu64(a: u64, b: u64) (u64, bool) = { + const res = a + b; + const overflow = res < a; + return (res, overflow); +}; + +@test fn addu64() void = { + const (res, overflow) = addu64(18446744073709551600, 10); + assert(res == 18446744073709551610); + assert(!overflow); + const (res, overflow) = addu64(18446744073709551610, 50); + assert(res == 44); + assert(overflow); +}; + +// Subtracts 'b' from 'a', returning the result and whether overflow occurred. +export fn subi8(a: i8, b: i8) (i8, bool) = addi8(a, -b); + +@test fn subi8() void = { + const (res, overflow) = subi8(-100, 20); + assert(res == -120); + assert(!overflow); + const (res, overflow) = subi8(-100, 50); + assert(res == 106); + assert(overflow); +}; + +// Subtracts 'b' from 'a', returning the result and whether overflow occurred. +export fn subi16(a: i16, b: i16) (i16, bool) = addi16(a, -b); + +@test fn subi16() void = { + const (res, overflow) = subi16(-32700, 60); + assert(res == -32760); + assert(!overflow); + const (res, overflow) = subi16(-32700, 100); + assert(res == 32736); + assert(overflow); +}; + +// Subtracts 'b' from 'a', returning the result and whether overflow occurred. +export fn subi32(a: i32, b: i32) (i32, bool) = addi32(a, -b); + +@test fn subi32() void = { + const (res, overflow) = subi32(-2147483600, 40); + assert(res == -2147483640); + assert(!overflow); + const (res, overflow) = subi32(-2147483600, 100); + assert(res == 2147483596); + assert(overflow); +}; + +// Subtracts 'b' from 'a', returning the result and whether overflow occurred. +export fn subi64(a: i64, b: i64) (i64, bool) = addi64(a, -b); + +@test fn subi64() void = { + const (res, overflow) = subi64(-9223372036854775800, 5); + assert(res == -9223372036854775805); + assert(!overflow); + const (res, overflow) = subi64(-9223372036854775800, 10); + assert(res == 9223372036854775806); + assert(overflow); +}; + +// Subtracts 'b' from 'a', returning the result and whether overflow occurred. +export fn subu8(a: u8, b: u8) (u8, bool) = { + const res = a - b; + const overflow = res > a; + return (res, overflow); +}; + +@test fn subu8() void = { + const (res, overflow) = subu8(250, 50); + assert(res == 200); + assert(!overflow); + const (res, overflow) = subu8(44, 100); + assert(res == 200); + assert(overflow); +}; + +// Subtracts 'b' from 'a', returning the result and whether overflow occurred. +export fn subu16(a: u16, b: u16) (u16, bool) = { + const res = a - b; + const overflow = res > a; + return (res, overflow); +}; + +@test fn subu16() void = { + const (res, overflow) = subu16(65530, 30); + assert(res == 65500); + assert(!overflow); + const (res, overflow) = subu16(14, 50); + assert(res == 65500); + assert(overflow); +}; + +// Subtracts 'b' from 'a', returning the result and whether overflow occurred. +export fn subu32(a: u32, b: u32) (u32, bool) = { + const res = a - b; + const overflow = res > a; + return (res, overflow); +}; + +@test fn subu32() void = { + const (res, overflow) = subu32(4294967290, 90); + assert(res == 4294967200); + assert(!overflow); + const (res, overflow) = subu32(4, 100); + assert(res == 4294967200); + assert(overflow); +}; + +// Subtracts 'b' from 'a', returning the result and whether overflow occurred. +export fn subu64(a: u64, b: u64) (u64, bool) = { + const res = a - b; + const overflow = res > a; + return (res, overflow); +}; + +@test fn subu64() void = { + const (res, overflow) = subu64(18446744073709551610, 10); + assert(res == 18446744073709551600); + assert(!overflow); + const (res, overflow) = subu64(44, 50); + assert(res == 18446744073709551610); + assert(overflow); +}; + +// Multiplies 'a' and 'b' returning the result and whether overflow occurred. +export fn muli8(a: i8, b: i8) (i8, bool) = { + const fullres = a: i16 * b: i16; + const res = fullres: i8; + const overflow = res != fullres; + return (res, overflow); +}; + +@test fn muli8() void = { + const (res, overflow) = muli8(11, 11); + assert(res == 121); + assert(!overflow); + const (res, overflow) = muli8(12, 12); + assert(res == -112); + assert(overflow); +}; + +// Multiplies 'a' and 'b' returning the result and whether overflow occurred. +export fn muli16(a: i16, b: i16) (i16, bool) = { + const fullres = a: i32 * b: i32; + const res = fullres: i16; + const overflow = res != fullres; + return (res, overflow); +}; + +@test fn muli16() void = { + const (res, overflow) = muli16(181, 181); + assert(res == 32761); + assert(!overflow); + const (res, overflow) = muli16(182, 182); + assert(res == -32412); + assert(overflow); +}; + +// Multiplies 'a' and 'b' returning the result and whether overflow occurred. +export fn muli32(a: i32, b: i32) (i32, bool) = { + const fullres = a: i64 * b: i64; + const res = fullres: i32; + const overflow = res != fullres; + return (res, overflow); +}; + +@test fn muli32() void = { + const (res, overflow) = muli32(46340, 46340); + assert(res == 2147395600); + assert(!overflow); + const (res, overflow) = muli32(46341, 46341); + assert(res == -2147479015); + assert(overflow); +}; + +// Multiplies 'a' and 'b' returning the result and whether overflow occurred. +export fn muli64(a: i64, b: i64) (i64, bool) = { + const (hi, lo) = math::mulu64(math::absi64(a), math::absi64(b)); + const res = a * b; + const overflow = hi != 0 || lo & (1 << 63) != 0; + return (res, overflow); +}; + +@test fn muli64() void = { + const (res, overflow) = muli64(3037000499, 3037000499); + assert(res == 9223372030926249001); + assert(!overflow); + const (res, overflow) = muli64(3037000500, 3037000500); + assert(res == -9223372036709301616); + assert(overflow); +}; + +// Multiplies 'a' and 'b' returning the result and whether overflow occurred. +export fn mulu8(a: u8, b: u8) (u8, bool) = { + const fullres = a: u16 * b: u16; + const res = fullres: u8; + const overflow = res != fullres; + return (res, overflow); +}; + +@test fn mulu8() void = { + const (res, overflow) = mulu8(15, 15); + assert(res == 225); + assert(!overflow); + const (res, overflow) = mulu8(16, 16); + assert(res == 0); + assert(overflow); +}; + +// Multiplies 'a' and 'b' returning the result and whether overflow occurred. +export fn mulu16(a: u16, b: u16) (u16, bool) = { + const fullres = a: u32 * b: u32; + const res = fullres: u16; + const overflow = res != fullres; + return (res, overflow); +}; + +@test fn mulu16() void = { + const (res, overflow) = mulu16(255, 255); + assert(res == 65025); + assert(!overflow); + const (res, overflow) = mulu16(256, 256); + assert(res == 0); + assert(overflow); +}; + +// Multiplies 'a' and 'b' returning the result and whether overflow occurred. +export fn mulu32(a: u32, b: u32) (u32, bool) = { + const fullres = a: u64 * b: u64; + const res = fullres: u32; + const overflow = res != fullres; + return (res, overflow); +}; + +@test fn mulu32() void = { + const (res, overflow) = mulu32(65535, 65535); + assert(res == 4294836225); + assert(!overflow); + const (res, overflow) = mulu32(65536, 65536); + assert(res == 0); + assert(overflow); +}; + +// Multiplies 'a' and 'b' returning the result and whether overflow occurred. +export fn mulu64(a: u64, b: u64) (u64, bool) = { + const (hi, lo) = math::mulu64(a, b); + const res = lo; + const overflow = hi != 0; + return (res, overflow); +}; + +@test fn mulu64() void = { + const (res, overflow) = mulu64(4294967295, 4294967295); + assert(res == 18446744065119617025); + assert(!overflow); + const (res, overflow) = mulu64(4294967296, 4294967296); + assert(res == 0); + assert(overflow); +}; diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib @@ -981,6 +981,11 @@ math() { gen_ssa math types rt } +math_checked() { + gen_srcs math::checked checked.ha + gen_ssa math::checked math +} + mime() { # This module is not built by default because gen-stdlib does not do a good # job of resolving @init dependency ordering issues @@ -1633,6 +1638,7 @@ linux::timerfd linux linux::vdso linux log linux freebsd math +math::checked math::complex math::random net linux freebsd diff --git a/stdlib.mk b/stdlib.mk @@ -589,6 +589,13 @@ stdlib_deps_any += $(stdlib_math_any) stdlib_math_linux = $(stdlib_math_any) stdlib_math_freebsd = $(stdlib_math_any) +# gen_lib math::checked (any) +stdlib_math_checked_any = $(HARECACHE)/math/checked/math_checked-any.o +stdlib_env += HARE_TD_math::checked=$(HARECACHE)/math/checked/math_checked.td +stdlib_deps_any += $(stdlib_math_checked_any) +stdlib_math_checked_linux = $(stdlib_math_checked_any) +stdlib_math_checked_freebsd = $(stdlib_math_checked_any) + # gen_lib math::complex (any) stdlib_math_complex_any = $(HARECACHE)/math/complex/math_complex-any.o stdlib_env += HARE_TD_math::complex=$(HARECACHE)/math/complex/math_complex.td @@ -1702,6 +1709,16 @@ $(HARECACHE)/math/math-any.ssa: $(stdlib_math_any_srcs) $(stdlib_rt) $(stdlib_ty @$(stdlib_env) $(HAREC) $(HAREFLAGS) -o $@ -Nmath \ -t$(HARECACHE)/math/math.td $(stdlib_math_any_srcs) +# math::checked (+any) +stdlib_math_checked_any_srcs = \ + $(STDLIB)/math/checked/checked.ha + +$(HARECACHE)/math/checked/math_checked-any.ssa: $(stdlib_math_checked_any_srcs) $(stdlib_rt) $(stdlib_math_$(PLATFORM)) + @printf 'HAREC \t$@\n' + @mkdir -p $(HARECACHE)/math/checked + @$(stdlib_env) $(HAREC) $(HAREFLAGS) -o $@ -Nmath::checked \ + -t$(HARECACHE)/math/checked/math_checked.td $(stdlib_math_checked_any_srcs) + # math::complex (+any) stdlib_math_complex_any_srcs = \ $(STDLIB)/math/complex/complex.ha @@ -2997,6 +3014,13 @@ testlib_deps_any += $(testlib_math_any) testlib_math_linux = $(testlib_math_any) testlib_math_freebsd = $(testlib_math_any) +# gen_lib math::checked (any) +testlib_math_checked_any = $(TESTCACHE)/math/checked/math_checked-any.o +testlib_env += HARE_TD_math::checked=$(TESTCACHE)/math/checked/math_checked.td +testlib_deps_any += $(testlib_math_checked_any) +testlib_math_checked_linux = $(testlib_math_checked_any) +testlib_math_checked_freebsd = $(testlib_math_checked_any) + # gen_lib math::complex (any) testlib_math_complex_any = $(TESTCACHE)/math/complex/math_complex-any.o testlib_env += HARE_TD_math::complex=$(TESTCACHE)/math/complex/math_complex.td @@ -4163,6 +4187,16 @@ $(TESTCACHE)/math/math-any.ssa: $(testlib_math_any_srcs) $(testlib_rt) $(testlib @$(testlib_env) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nmath \ -t$(TESTCACHE)/math/math.td $(testlib_math_any_srcs) +# math::checked (+any) +testlib_math_checked_any_srcs = \ + $(STDLIB)/math/checked/checked.ha + +$(TESTCACHE)/math/checked/math_checked-any.ssa: $(testlib_math_checked_any_srcs) $(testlib_rt) $(testlib_math_$(PLATFORM)) + @printf 'HAREC \t$@\n' + @mkdir -p $(TESTCACHE)/math/checked + @$(testlib_env) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nmath::checked \ + -t$(TESTCACHE)/math/checked/math_checked.td $(testlib_math_checked_any_srcs) + # math::complex (+any) testlib_math_complex_any_srcs = \ $(STDLIB)/math/complex/complex.ha \