diff --git a/rules/rules.go b/rules/rules.go index e382a60ed454cfeecf5cd00a939fce6be8e64fd1..a66a97f813e0013700c29ee3da86593dad050463 100644 --- a/rules/rules.go +++ b/rules/rules.go @@ -3,6 +3,7 @@ package rules import ( "errors" + "fmt" "github.com/ichiban/prolog" ) @@ -14,6 +15,9 @@ type Engine struct { var ( prologKinds = ` + unit_kind(f, temperature). + unit_kind(k, temperature). + unit_kind(c, temperature). unit_kind(m2, area). unit_kind(cm2, area). unit_kind(cm, distance). @@ -28,9 +32,22 @@ var ( unit_kind(butter, density). unit_kind(water, density). ` - prologKindsConvert = ` - convert_kind(mass, volume, density). - convert_kind(volume, area, distance). + prologConvert = ` +rule(GivenQuant, f, TargetQuant, c) :- + TargetQuant is (GivenQuant - 32) * 5 / 9. +rule(GivenQuant, c, TargetQuant, f) :- + TargetQuant is (GivenQuant / 5 * 9) + 32. +rule(GivenQuant, c, TargetQuant, k) :- + TargetQuant is GivenQuant + 273.15. +rule(GivenQuant, k, TargetQuant, c) :- + TargetQuant is GivenQuant - 273.15. +rule(GivenQuant, GivenUnit, TargetQuant, TargetUnit) :- + rule(GivenQuant, GivenUnit, X, Y), + rule(X, Y, TargetQuant, TargetUnit). +convert(GivenQuant, GivenUnit, TargetQuant, TargetUnit) :- + unit_kind(GivenUnit, temperature), + unit_kind(TargetUnit, temperature), + rule(GivenQuant, GivenUnit, TargetQuant, TargetUnit). ` ) @@ -48,6 +65,10 @@ func New() (*Engine, error) { return nil, err } + if err := ngn.p.Exec(prologConvert); err != nil { + return nil, err + } + return ngn, nil } @@ -74,3 +95,25 @@ func (ngn *Engine) Kind(unit string) (string, error) { } return result, nil } + +// Convert converts units. +func (ngn *Engine) Convert(qty float64, givenUnit, targetUnit string) (float64, error) { + sols, err := ngn.p.Query("convert(?, ?, Qty, ?).", qty, givenUnit, targetUnit) + if err != nil { + fmt.Println("Huh?") + return 0, err + } + defer sols.Close() + + for sols.Next() { + var quantity struct { + Qty float64 + } + if err := sols.Scan(&quantity); err != nil { + fmt.Println("Scan?") + return 0, err + } + return quantity.Qty, err + } + return 0, err +} diff --git a/rules/rules_test.go b/rules/rules_test.go index f9d244ea9161708ea28e2aa7f96dbdd865c2babf..a3867b94fd908900a1fb4cc92f7eadb654c6de9c 100644 --- a/rules/rules_test.go +++ b/rules/rules_test.go @@ -30,3 +30,32 @@ func TestKinds(t *testing.T) { }) } } + +func TestConverts(t *testing.T) { + cases := []struct { + name string + qty float64 + given string + target string + result float64 + }{ + { + "c to f", + 22.0, + "c", + "f", + 71.6, + }, + } + + ngn, err := New() + assert.NoError(t, err) + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + result, err := ngn.Convert(tc.qty, tc.given, tc.target) + assert.NoError(t, err) + + assert.InDelta(t, tc.result, result, 0.1) + }) + } +}