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

(function () {
    'use strict';
 
    // GENERIC FUNCTIONS
 
    // concatMap :: (a -> [b]) -> [a] -> [b]
    var concatMap = function concatMap(f, xs) {
            return [].concat.apply([], xs.map(f));
        },
 
        // curry :: ((a, b) -> c) -> a -> b -> c
        curry = function curry(f) {
            return function (a) {
                return function (b) {
                    return f(a, b);
                };
            };
        },
 
        // intersectBy :: (a - > a - > Bool) - > [a] - > [a] - > [a]
        intersectBy = function intersectBy(eq, xs, ys) {
            return xs.length && ys.length ? xs.filter(function (x) {
                return ys.some(curry(eq)(x));
            }) : [];
        },
 
        // range :: Int -> Int -> Maybe Int -> [Int]
        range = function range(m, n, step) {
            var d = (step || 1) * (n >= m ? 1 : -1);
            return Array.from({
                length: Math.floor((n - m) / d) + 1
            }, function (_, i) {
                return m + i * d;
            });
        };
 
    // PROBLEM FUNCTIONS
 
    // add, mul :: (Int, Int) -> Int
    var add = function add(xy) {
            return xy[0] + xy[1];
        },
        mul = function mul(xy) {
            return xy[0] * xy[1];
        };
 
    // sumEq, mulEq :: (Int, Int) -> [(Int, Int)]
    var sumEq = function sumEq(p) {
            var addP = add(p);
            return s1.filter(function (q) {
                return add(q) === addP;
            });
        },
        mulEq = function mulEq(p) {
            var mulP = mul(p);
            return s1.filter(function (q) {
                return mul(q) === mulP;
            });
        };
 
    // pairEQ :: ((a, a) -> (a, a)) -> Bool
    var pairEQ = function pairEQ(a, b) {
        return a[0] === b[0] && a[1] === b[1];
    };
 
    // MAIN
 
    // xs :: [Int]
    var xs = range(1, 100);
 
    // s1 s2, s3, s4 :: [(Int, Int)]
    var s1 = concatMap(function (x) {
            return concatMap(function (y) {
                return 1 < x && x < y && x + y < 100 ? [
                    [x, y]
                ] : [];
            }, xs);
        }, xs),
 
        s2 = s1.filter(function (p) {
            return sumEq(p).every(function (q) {
                return mulEq(q).length > 1;
            });
        }),
 
        s3 = s2.filter(function (p) {
            return intersectBy(pairEQ, mulEq(p), s2).length === 1;
        }),
 
        s4 = s3.filter(function (p) {
            return intersectBy(pairEQ, sumEq(p), s3).length === 1;
        });
 
    return s4;
})();
 