<commit_before>package floatutils_test

import (
	"math"
	"math/big"
	"math/rand"
	"testing"

	"github.com/ALTree/floatutils"
)

func TestLog(t *testing.T) {
	for _, test := range []struct {
		x    string
		prec uint
		want string
	}{
		// 80 decimal digits are enough to give us 250 binary digits when parsed by the Parse function
		{"0.5", 250, "-0.6931471805599453094172321214581765680755001343602552541206800094933936219696947"},
		{"0.25", 250, "-1.3862943611198906188344642429163531361510002687205105082413600189867872439393894"},
		{"0.0125", 250, "-4.3820266346738816122696878190588939118276018917095387383953679294477534755864366"},

		{"1", 250, "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000"},
		{"2", 250, "0.6931471805599453094172321214581765680755001343602552541206800094933936219696947"},
		{"10", 250, "2.3025850929940456840179914546843642076011014886287729760333279009675726096773524"},
		{"512", 250, "6.2383246250395077847550890931235891126795012092422972870861200854405425977272524"},
		{"42e42", 250, "100.4462435240332870346734711985671787216063379441733308877145363337065173843891771"},
	} {
		want := new(big.Float).SetPrec(test.prec)
		want.Parse(test.want, 10)

		x := new(big.Float).SetPrec(test.prec)
		x.Parse(test.x, 10)

		z := floatutils.Log(x)

		// test if precision is correctly set
		if z.Prec() != test.prec {
			t.Errorf("Log(%v): got %d prec, want %d prec", x, z.Prec(), test.prec)
		}

		// test returned value
		if !compareFloats(want, z, test.prec, t) {
			t.Errorf("Log(%v): error is too big.\nwant = %.100e\ngot  = %.100e\n", x, z, want)
		}
	}
}

func TestLog32(t *testing.T) {
	for i := 0; i < 1e4; i++ {
		r := rand.Float32() * 1e3
		x := big.NewFloat(float64(r)).SetPrec(24)
		z, acc := floatutils.Log(x).Float32()
		want := math.Log(float64(r))
		if z != float32(want) || acc != big.Exact {
			t.Errorf("Log(%f) =\n got %b (%s);\nwant %b (Exact)", x, z, acc, want)
		}
	}
}

func TestLog32Small(t *testing.T) {
	for i := 0; i < 1e4; i++ {
		r := rand.Float32() * 1e-30
		x := big.NewFloat(float64(r)).SetPrec(24)
		z, acc := floatutils.Log(x).Float32()
		want := math.Log(float64(r))
		if z != float32(want) || acc != big.Exact {
			t.Errorf("Log(%f) =\n got %b (%s);\nwant %b (Exact)", x, z, acc, want)
		}
	}
}

func TestLog32Big(t *testing.T) {
	for i := 0; i < 1e4; i++ {
		r := rand.Float32() * 1e30
		x := big.NewFloat(float64(r)).SetPrec(24)
		z, acc := floatutils.Log(x).Float32()
		want := math.Log(float64(r))
		if z != float32(want) || acc != big.Exact {
			t.Errorf("Log(%f) =\n got %b (%s);\nwant %b (Exact)", x, z, acc, want)
		}
	}
}

// Unfortunately, the Go math.Log function is not completely
// accurate, so it doesn't make sense to require 100%
// compatibility with it, since it happens that math.Log
// returns a result with the last bit off (see Issue #9546).
//
// For this reason, we just require that the result is
// within distance 1e-14 from what math.Log returns
// (1e-12 for very small values).
func TestLog64(t *testing.T) {
	for i := 0; i < 1e4; i++ {
		r := rand.Float64() * 1e3
		x := big.NewFloat(r).SetPrec(53)
		z, acc := floatutils.Log(x).Float64()
		want := math.Log(r)
		if math.Abs(z-want) > 1e-14 || acc != big.Exact {
			t.Errorf("Log(%g) =\n got %g (%s);\nwant %g (Exact)", x, z, acc, want)
		}
	}
}

func TestLog64Small(t *testing.T) {
	for i := 0; i < 1e4; i++ {
		r := rand.Float64() * 1e-300
		x := big.NewFloat(r).SetPrec(53)
		z, acc := floatutils.Log(x).Float64()
		want := math.Log(r)
		if math.Abs(z-want) > 1e-12 || acc != big.Exact { // 1e-12 for very small values
			t.Errorf("Log(%g) =\n got %g (%s);\nwant %g (Exact)", x, z, acc, want)
		}
	}
}

func TestLog64Big(t *testing.T) {
	for i := 0; i < 1e4; i++ {
		r := rand.Float64() * 1e300
		x := big.NewFloat(r).SetPrec(53)
		z, acc := floatutils.Log(x).Float64()
		want := math.Log(r)
		if math.Abs(z-want) > 1e-14 || acc != big.Exact {
			t.Errorf("Log(%g) =\n got %g (%s);\nwant %g (Exact)", x, z, acc, want)
		}
	}
}

func TestLogSpecialValues(t *testing.T) {
	for i, f := range []float64{
		+0.0,
		-0.0,
		math.Inf(+1),
	} {
		x := big.NewFloat(f).SetPrec(53)
		z, acc := floatutils.Log(x).Float64()
		want := math.Log(f)
		if z != want || acc != big.Exact {
			t.Errorf("%d) Log(%f) =\n got %b (%s);\nwant %b (Exact)", i, f, z, acc, want)
		}
	}
}

// ---------- Benchmarks ----------

func benchmarkLog(num float64, prec uint, b *testing.B) {
	b.ReportAllocs()
	x := new(big.Float).SetPrec(prec).SetFloat64(num)
	var f *big.Float
	for n := 0; n < b.N; n++ {
		f = floatutils.Log(x)
	}

	result = f
}

func BenchmarkLog2Prec10(b *testing.B)    { benchmarkLog(2, 1e1, b) }
func BenchmarkLog2Prec100(b *testing.B)   { benchmarkLog(2, 1e2, b) }
func BenchmarkLog2Prec1000(b *testing.B)  { benchmarkLog(2, 1e3, b) }<commit_msg>log_test.go: far more tests, with a range of precisions<commit_after>package floatutils_test

import (
	"math"
	"math/big"
	"math/rand"
	"testing"

	"github.com/ALTree/floatutils"
)

func TestLog(t *testing.T) {
	for _, test := range []struct {
		x    string
		want string
	}{
		// 350 decimal digits are enough to give us up to 1000 binary digits
		{"0.5", "-0.69314718055994530941723212145817656807550013436025525412068000949339362196969471560586332699641868754200148102057068573368552023575813055703267075163507596193072757082837143519030703862389167347112335011536449795523912047517268157493206515552473413952588295045300709532636664265410423915781495204374043038550080194417064167151864471283996817178454696"},

		{"0.25", "-1.3862943611198906188344642429163531361510002687205105082413600189867872439393894312117266539928373750840029620411413714673710404715162611140653415032701519238614551416567428703806140772477833469422467002307289959104782409503453631498641303110494682790517659009060141906527332853082084783156299040874808607710016038883412833430372894256799363435690939"},

		{"0.0125", "-4.3820266346738816122696878190588939118276018917095387383953679294477534755864366270535871860788543609679722271039983058344660861723571984277642996240040095752750899208106689864147210106979082189417635556550588715983462075888842670124944153533207460860520530946333864410280342429017041970928492563533263928706772062013203262792640026952942261381891629"},

		{"1e-100", "-230.25850929940456840179914546843642076011014886287729760333279009675726096773524802359972050895982983419677840422862486334095254650828067566662873690987816894829072083255546808437998948262331985283935053089653777326288461633662222876982198867465436674744042432743651550489343149393914796194044002221051017141748003688084012647080685567743216228355220"},

		{"1e-100000", "-230258.50929940456840179914546843642076011014886287729760333279009675726096773524802359972050895982983419677840422862486334095254650828067566662873690987816894829072083255546808437998948262331985283935053089653777326288461633662222876982198867465436674744042432743651550489343149393914796194044002221051017141748003688084012647080685567743216228355220"},

		{"1", "0.0"},

		{"2", "0.69314718055994530941723212145817656807550013436025525412068000949339362196969471560586332699641868754200148102057068573368552023575813055703267075163507596193072757082837143519030703862389167347112335011536449795523912047517268157493206515552473413952588295045300709532636664265410423915781495204374043038550080194417064167151864471283996817178454696"},

		{"10", "2.3025850929940456840179914546843642076011014886287729760333279009675726096773524802359972050895982983419677840422862486334095254650828067566662873690987816894829072083255546808437998948262331985283935053089653777326288461633662222876982198867465436674744042432743651550489343149393914796194044002221051017141748003688084012647080685567743216228355220"},

		{"1e5", "11.512925464970228420089957273421821038005507443143864880166639504837863048386762401179986025447991491709838920211431243167047627325414033783331436845493908447414536041627773404218999474131165992641967526544826888663144230816831111438491099433732718337372021216371825775244671574696957398097022001110525508570874001844042006323540342783871608114177610"},

		{"1e10", "23.025850929940456840179914546843642076011014886287729760333279009675726096773524802359972050895982983419677840422862486334095254650828067566662873690987816894829072083255546808437998948262331985283935053089653777326288461633662222876982198867465436674744042432743651550489343149393914796194044002221051017141748003688084012647080685567743216228355220"},

		{"1e100", "230.25850929940456840179914546843642076011014886287729760333279009675726096773524802359972050895982983419677840422862486334095254650828067566662873690987816894829072083255546808437998948262331985283935053089653777326288461633662222876982198867465436674744042432743651550489343149393914796194044002221051017141748003688084012647080685567743216228355220"},

		{"1e100000", "230258.50929940456840179914546843642076011014886287729760333279009675726096773524802359972050895982983419677840422862486334095254650828067566662873690987816894829072083255546808437998948262331985283935053089653777326288461633662222876982198867465436674744042432743651550489343149393914796194044002221051017141748003688084012647080685567743216228355220"},
	} {
		for _, prec := range []uint{24, 53, 64, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000} {
			want := new(big.Float).SetPrec(prec)
			want.Parse(test.want, 10)

			x := new(big.Float).SetPrec(prec)
			x.Parse(test.x, 10)

			z := floatutils.Log(x)

			// test if the precision is correctly set
			if z.Prec() != prec {
				t.Errorf("Log(%v): got %d prec, want %d prec", x, z.Prec(), prec)
			}

			// test returned value
			if !compareFloats(want, z, prec, t) {
				t.Errorf("Log(%v): error is too big.\nwant = %.100e\ngot  = %.100e\n", x, z, want)
			}
		}
	}
}

func TestLog32(t *testing.T) {
	for i := 0; i < 1e4; i++ {
		r := rand.Float32() * 1e3
		x := big.NewFloat(float64(r)).SetPrec(24)
		z, acc := floatutils.Log(x).Float32()
		want := math.Log(float64(r))
		if z != float32(want) || acc != big.Exact {
			t.Errorf("Log(%f) =\n got %b (%s);\nwant %b (Exact)", x, z, acc, want)
		}
	}
}

func TestLog32Small(t *testing.T) {
	for i := 0; i < 1e4; i++ {
		r := rand.Float32() * 1e-30
		x := big.NewFloat(float64(r)).SetPrec(24)
		z, acc := floatutils.Log(x).Float32()
		want := math.Log(float64(r))
		if z != float32(want) || acc != big.Exact {
			t.Errorf("Log(%f) =\n got %b (%s);\nwant %b (Exact)", x, z, acc, want)
		}
	}
}

func TestLog32Big(t *testing.T) {
	for i := 0; i < 1e4; i++ {
		r := rand.Float32() * 1e30
		x := big.NewFloat(float64(r)).SetPrec(24)
		z, acc := floatutils.Log(x).Float32()
		want := math.Log(float64(r))
		if z != float32(want) || acc != big.Exact {
			t.Errorf("Log(%f) =\n got %b (%s);\nwant %b (Exact)", x, z, acc, want)
		}
	}
}

// Unfortunately, the Go math.Log function is not completely
// accurate, so it doesn't make sense to require 100%
// compatibility with it, since it happens that math.Log
// returns a result with the last bit off (see Issue #9546).
//
// For this reason, we just require that the result is
// within distance 1e-14 from what math.Log returns
// (1e-12 for very small values).
func TestLog64(t *testing.T) {
	for i := 0; i < 1e4; i++ {
		r := rand.Float64() * 1e3
		x := big.NewFloat(r).SetPrec(53)
		z, acc := floatutils.Log(x).Float64()
		want := math.Log(r)
		if math.Abs(z-want) > 1e-14 || acc != big.Exact {
			t.Errorf("Log(%g) =\n got %g (%s);\nwant %g (Exact)", x, z, acc, want)
		}
	}
}

func TestLog64Small(t *testing.T) {
	for i := 0; i < 1e4; i++ {
		r := rand.Float64() * 1e-300
		x := big.NewFloat(r).SetPrec(53)
		z, acc := floatutils.Log(x).Float64()
		want := math.Log(r)
		if math.Abs(z-want) > 1e-12 || acc != big.Exact { // 1e-12 for very small values
			t.Errorf("Log(%g) =\n got %g (%s);\nwant %g (Exact)", x, z, acc, want)
		}
	}
}

func TestLog64Big(t *testing.T) {
	for i := 0; i < 1e4; i++ {
		r := rand.Float64() * 1e300
		x := big.NewFloat(r).SetPrec(53)
		z, acc := floatutils.Log(x).Float64()
		want := math.Log(r)
		if math.Abs(z-want) > 1e-14 || acc != big.Exact {
			t.Errorf("Log(%g) =\n got %g (%s);\nwant %g (Exact)", x, z, acc, want)
		}
	}
}

func TestLogSpecialValues(t *testing.T) {
	for i, f := range []float64{
		+0.0,
		-0.0,
		math.Inf(+1),
	} {
		x := big.NewFloat(f).SetPrec(53)
		z, acc := floatutils.Log(x).Float64()
		want := math.Log(f)
		if z != want || acc != big.Exact {
			t.Errorf("%d) Log(%f) =\n got %b (%s);\nwant %b (Exact)", i, f, z, acc, want)
		}
	}
}

// ---------- Benchmarks ----------

func benchmarkLog(num float64, prec uint, b *testing.B) {
	b.ReportAllocs()
	x := new(big.Float).SetPrec(prec).SetFloat64(num)
	var f *big.Float
	for n := 0; n < b.N; n++ {
		f = floatutils.Log(x)
	}

	result = f
}

func BenchmarkLog2Prec53(b *testing.B)    { benchmarkLog(2, 53, b) }
func BenchmarkLog2Prec64(b *testing.B)    { benchmarkLog(2, 64, b) }
func BenchmarkLog2Prec100(b *testing.B)   { benchmarkLog(2, 1e2, b) }
func BenchmarkLog2Prec250(b *testing.B)   { benchmarkLog(2, 250, b) }
func BenchmarkLog2Prec500(b *testing.B)   { benchmarkLog(2, 500, b) }
func BenchmarkLog2Prec1000(b *testing.B)  { benchmarkLog(2, 1e3, b) }
{'fraction_non_alphanumeric': 0.14429479418886199, 'fraction_numerical': 0.3784049636803874, 'mean_word_length': 5.065626434144103, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 1, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7336388', 'n_tokens_mistral': 8770, 'n_tokens_neox': 5555, 'n_words': 957}