Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

fraction_test.gno

2.76 Kb · 99 lines
 1package fraction
 2
 3import (
 4	"testing"
 5
 6	"gno.land/p/nt/uassert/v0"
 7)
 8
 9func mk(t *testing.T, n, d int64) Fraction {
10	t.Helper()
11	f, err := New(n, d)
12	uassert.NoError(t, err)
13	return f
14}
15
16func TestNewReduces(t *testing.T) {
17	uassert.Equal(t, "1/2", mk(t, 2, 4).String())
18	uassert.Equal(t, "2", mk(t, 6, 3).String())
19	uassert.Equal(t, "0", mk(t, 0, 5).String())
20}
21
22// The sign always lives in the numerator, so -1/2 and 1/-2 are the same value.
23func TestSignNormalised(t *testing.T) {
24	a := mk(t, 1, -2)
25	uassert.Equal(t, "-1/2", a.String())
26	uassert.True(t, a.Equal(mk(t, -1, 2)))
27	uassert.True(t, mk(t, -2, -4).Equal(mk(t, 1, 2)))
28}
29
30func TestZeroDenominator(t *testing.T) {
31	_, err := New(1, 0)
32	uassert.ErrorIs(t, err, ErrZeroDenominator)
33}
34
35// The headline: exact arithmetic where floats would drift.
36func TestExactThirds(t *testing.T) {
37	third := mk(t, 1, 3)
38	sum, ok := third.Add(third)
39	uassert.True(t, ok)
40	sum, ok = sum.Add(third)
41	uassert.True(t, ok)
42	uassert.Equal(t, "1", sum.String()) // exactly 1, not 0.9999…
43}
44
45func TestArithmetic(t *testing.T) {
46	a, b := mk(t, 1, 2), mk(t, 1, 3)
47	s, _ := a.Add(b)
48	uassert.Equal(t, "5/6", s.String())
49	d, _ := a.Sub(b)
50	uassert.Equal(t, "1/6", d.String())
51	m, _ := a.Mul(b)
52	uassert.Equal(t, "1/6", m.String())
53	q, _ := a.Div(b)
54	uassert.Equal(t, "3/2", q.String())
55	uassert.Equal(t, "-1/2", a.Neg().String())
56}
57
58func TestDivByZero(t *testing.T) {
59	_, ok := mk(t, 1, 2).Div(Zero())
60	uassert.False(t, ok)
61}
62
63// Overflow must be reported, never silently wrapped into a wrong answer.
64func TestOverflowReported(t *testing.T) {
65	big := Fraction{num: 1 << 62, den: 3}
66	_, ok := big.Mul(Fraction{num: 1 << 62, den: 5})
67	uassert.False(t, ok)
68	_, ok2 := big.Add(Fraction{num: 1 << 62, den: 7})
69	uassert.False(t, ok2)
70}
71
72// Comparison is exact: cross-multiplication, never a decimal detour.
73func TestCmp(t *testing.T) {
74	uassert.Equal(t, -1, mk(t, 1, 3).Cmp(mk(t, 1, 2)))
75	uassert.Equal(t, 1, mk(t, 2, 3).Cmp(mk(t, 1, 2)))
76	uassert.Equal(t, 0, mk(t, 2, 4).Cmp(mk(t, 1, 2)))
77	// 1/3 vs 33333/100000 — a decimal comparison at 5 places would call these equal
78	uassert.Equal(t, 1, mk(t, 1, 3).Cmp(mk(t, 33333, 100000)))
79}
80
81func TestDecimal(t *testing.T) {
82	uassert.Equal(t, "0.333", Decimal(mk(t, 1, 3), 3))
83	uassert.Equal(t, "0.5", Decimal(mk(t, 1, 2), 1))
84	uassert.Equal(t, "1", Decimal(mk(t, 3, 2), 0)) // truncates toward zero
85	uassert.Equal(t, "-0.333", Decimal(mk(t, -1, 3), 3))
86	uassert.Equal(t, "2.000", Decimal(Int(2), 3))
87	uassert.Equal(t, "0", Decimal(Zero(), 0))
88}
89
90// The zero value must behave as 0/1 rather than dividing by zero.
91func TestZeroValueUsable(t *testing.T) {
92	var f Fraction
93	uassert.Equal(t, int64(1), f.Den())
94	uassert.True(t, f.IsZero())
95	uassert.Equal(t, "0", f.String())
96	s, ok := f.Add(Int(1))
97	uassert.True(t, ok)
98	uassert.Equal(t, "1", s.String())
99}