Files
fejkdata/internal/proven/proven_test.go
T

86 lines
3.2 KiB
Go

package proven
import (
"math"
"testing"
"github.com/larvit/fejkdata/internal/datatype"
"github.com/larvit/fejkdata/internal/grammar"
)
func TestLiteralSaysWhichDatatypesItsTextIs(t *testing.T) {
for _, c := range []struct {
text string
not [datatype.Count]bool
}{
{"42", [datatype.Count]bool{datatype.Boolean: true}},
{"1.5", [datatype.Count]bool{datatype.Integer: true, datatype.Boolean: true}},
{"true", [datatype.Count]bool{datatype.Integer: true, datatype.Number: true}},
{"007", [datatype.Count]bool{datatype.Integer: true, datatype.Number: true, datatype.Boolean: true}},
{"-0", [datatype.Count]bool{datatype.Boolean: true}},
} {
v := Literal(c.text)
for d := datatype.Integer; d < datatype.Count; d++ {
if (v.Not[d] != "") != c.not[d] {
t.Errorf("Literal(%q).Not[%s] = %q", c.text, d, v.Not[d])
}
}
}
}
func TestOrKeepsTheBoundsOfBothAndTheFirstReason(t *testing.T) {
v := Bounded(1, 2, true).Or(Bounded(-3, 0.5, false))
if v.Lo != -3 || v.Hi != 2 || v.Integral || v.NonZero != 0 {
t.Errorf("Or = %+v, want [-3, 2], not integral, possibly zero", v)
}
if v := Unproven("first").Or(Unproven("second")); v.NotOperand != "first" || v.Not[datatype.Number] != "first" {
t.Errorf("Or = %+v, want the first reason kept", v)
}
}
func TestOfCalcBoundsAnExpressionFromItsOperands(t *testing.T) {
operand := func(name string) Facts {
return map[string]Facts{"a": Bounded(1, 9, true), "b": Bounded(0, 9, true), "big": Bounded(0, 1e299, true), "word": Unproven("it is text")}[name]
}
for expr, want := range map[string]string{
"a * 2 + 1": "",
"a / b": "divides by b, which is not proven nonzero",
"big * 100": "(big * 100) is not proven within 1e300",
"big": "",
"word + 1": `operand "word": it is text`,
"-(a - 10)": "",
} {
c, err := grammar.ParseCalc(expr)
if err != nil {
t.Fatal(err)
}
v, doubt := OfCalc(c.Expr, operand)
if doubt != want {
t.Errorf("OfCalc(%s) doubts %q, want %q", expr, doubt, want)
}
if expr == "a * 2 + 1" && (v.Lo != 3 || v.Hi != 19 || !v.Integral || v.NonZero != 3) {
t.Errorf("OfCalc(%s) = %+v, want [3, 19], integral, nonzero by 3", expr, v)
}
if expr == "-(a - 10)" && (v.Lo != 1 || v.Hi != 9) {
t.Errorf("OfCalc(%s) = %+v, want [1, 9]", expr, v)
}
}
lone, _ := grammar.ParseCalc("x")
if _, doubt := OfCalc(lone.Expr, func(string) Facts { return Bounded(0, 1e301, true) }); doubt != "x is not proven within 1e300" {
t.Errorf("OfCalc(x) doubts %q, want a lone operand held to the limit", doubt)
}
}
func TestPrintedNumberWidensItsBoundsByTheRounding(t *testing.T) {
v := PrintedNumber("float(0,1,1)", Bounded(0.2, 0.8, false), 1)
if math.Abs(v.Lo-0.15) > 1e-12 || math.Abs(v.Hi-0.85) > 1e-12 || v.Not[datatype.Integer] == "" || v.Not[datatype.Number] != "" {
t.Errorf("PrintedNumber = %+v, want [0.15, 0.85], a number and no integer", v)
}
if v := PrintedNumber("calc(a)", Bounded(1, 2, true), ShortestDecimals); v.Not[datatype.Integer] != "" {
t.Errorf("PrintedNumber = %+v, want a whole value printed as an integer", v)
}
if v := Printing("digits(3)", datatype.String, Bounded(0, 999, true)); v.Not[datatype.Integer] != "{digits(3)} prints text, not an integer" {
t.Errorf("Printing = %q, want the text named", v.Not[datatype.Integer])
}
}