View Source Pythagorean Triples

Mix.install([
  {:guesswork, "~> 0.6"},
  {:kino, "~> 0.13"}
],
  consolidate_protocols: false
)

Queries that Calculate Values

import Guesswork.Ast

alias Guesswork.Ast.And
alias Guesswork.Ast.Assign
alias Guesswork.Ast.OneOf
alias Guesswork.Answer.Result
alias Guesswork.Query

Queries don't necisarily need facts to operate (in fact, Guesswork.query/2 will just use an empty collection), they can use assigned values, and then calculate new values. The below example is a really simple query that uses Guesswork.Ast.Assign to assign values to a and b, and then uses is to calculated a third value based on the first two.

It should be noted that, in most cases, statements are created with functions. But the is macro is needed to build a Guesswork.Ast.Is.

Guesswork.query(
  term(And.new([Assign.new(a, 1), Assign.new(b, 2), is(d, fn a, b -> b - a end)])),
  10
)

Below is a more complex example, calculating pythagorean triples. Instead of using set we use one_of to assign an enumerable to a variable, so that the variable can be one of any of the values. Then we use is again, but this time to test that our variables really are pythagorean triples.

Guesswork.query(
  term(
    And.new([
      OneOf.new(a, 1..10),
      OneOf.new(b, 1..10),
      OneOf.new(c, 1..10),
      is(true, fn a, b -> a < b end),
      is(0, fn a, b, c -> Integer.pow(c, 2) - Integer.pow(a, 2) - Integer.pow(b, 2) end)
    ])
  ),
  10
)

But one_of will support not just finite enumerables, but streams. So below we calculate the first ten triples for all positive integers.

Its worth noting that here, instead of using just using the query function, we create an unresolved query, and then run it. This allows us to reuse the query below.

query =
  Query.new(
    term(
      And.new([
        OneOf.new(a, Stream.iterate(1, &(&1 + 1))),
        OneOf.new(b, Stream.iterate(1, &(&1 + 1))),
        OneOf.new(c, Stream.iterate(1, &(&1 + 1))),
        is(true, fn a, b -> a < b end),
        is(0, fn a, b, c -> Integer.pow(c, 2) - Integer.pow(a, 2) - Integer.pow(b, 2) end)
      ])
    )
  )

Result.run(query, 10)

Now that we have the unresolved query we can calculate even more, and pull the first 100. Note that this will take a few seconds. The process is brute force, so as you request more results, more combinations of a, b, and c will have to be tested.

Result.run(query, 100)