Task[edit]
Solve the "Impossible Puzzle":


X and Y are two different whole numbers greater than 1. Their sum is no greater than 100, and Y is greater than X. S and P are two mathematicians (and consequently perfect logicians); S knows the sum X+Y and P knows the product X*Y. Both S and P know all the information in this paragraph.
The following conversation occurs:

 S says "P does not know X and Y."
 P says "Now I know X and Y."
 S says "Now I also know X and Y!"
What are X and Y?


Guidance
It can be hard to wrap one's head around what the three lines of dialog between S (the "sum guy") and P (the "product guy") convey about the values of X and Y.

So for your convenience, here's a break-down:





 Quote

 Implied fact


 1)

 S says "P does not know X and Y."

 For every possible sum decomposition of the number X+Y, the product has in turn more than one product decomposition.


 2)

 P says "Now I know X and Y."

 The number X*Y has only one product decomposition for which fact 1 is true.


 3)

 S says "Now I also know X and Y."

 The number X+Y has only one sum decomposition for which fact 2 is true.

Terminology:

 "sum decomposition" of a number = Any pair of positive integers (A, B) so that A+B equals the number. Here, with the additional constraint 2 ≤ A < B.
 "product decomposition" of a number = Any pair of positive integers (A, B) so that A*B equals the number. Here, with the additional constraint 2 ≤ A < B.


Your program can solve the puzzle by considering all possible pairs (X, Y) in the range 2 ≤ X < Y ≤ 98, and then successively eliminating candidates based on the three facts. It turns out only one solution remains!

See the Python example for an implementation that uses this approach with a few optimizations.

See also
   Wikipedia:   Sum and Product Puzzle

import "/dynamic" for Tuple
import "/seq" for Lst
 
var P = Tuple.create("P", ["x", "y", "sum", "prod"])
 
var intersect = Fn.new { |l1, l2|
    var l3 = (l1.count < l2.count) ? l1 : l2
    var l4 = (l3 == l1) ? l2 : l1
    var l5 = []
    for (e in l3) if (l4.contains(e)) l5.add(e)
    return l5
}
 
var candidates = []
for (x in 2..49) {
    for (y in x + 1..100 - x) {
        candidates.add(P.new(x, y, x + y, x * y))
    }
}
 
var sumGroups  = Lst.groups(candidates) { |c| c.sum }
var prodGroups = Lst.groups(candidates) { |c| c.prod }
var sumMap = {}
for (sumGroup in sumGroups) {
    sumMap[sumGroup[0]] = sumGroup[1].map { |l| l[0] }.toList
}
var prodMap = {}
for (prodGroup in prodGroups) {
    prodMap[prodGroup[0]] = prodGroup[1].map { |l| l[0] }.toList
}
var fact1 = candidates.where { |c| sumMap[c.sum].all { |c| prodMap[c.prod].count > 1 } }.toList
var fact2 = fact1.where { |c| intersect.call(prodMap[c.prod], fact1).count == 1 }.toList
var fact3 = fact2.where { |c| intersect.call(sumMap[c.sum], fact2).count == 1 }.toList
System.write("The only solution is : ")
for (p in fact3) System.print("x = %(p.x), y = %(p.y)")