Alice, Samantha, and Patricia are relaxing on the porch, when Alice suddenly says: _"I'm thinking of two numbers, both greater than or equal to 2. I shall tell Samantha the sum of the two numbers and Patricia the product of the two numbers."_ 

She takes Samantha aside and whispers in her ear the sum so that Patricia cannot hear it. Then she takes Patricia aside and whispers in her ear the product so that Samantha cannot hear it.

After a moment of reflection, Samantha says:

 **Statement 1:** _"Patricia cannot know what the two numbers are."_

After which Patricia says:

 **Statement 2:**  _"In that case, I do know what the two numbers are."_

To which Samantha replies:

 **Statement 3:**  _"Then I too know what the two numbers are."_

Your first task is to write a function `statement1(s)` that takes an `int` argument `s` and returns `True` if and only if Samantha could have made statement 1 if given the number `s`. You may assume that `s` is the sum of two numbers both greater than or equal to 2.  

Your second task is to write a function `statement2(p)` that takes an `int` argument `p` and returns `True` if and only if Patricia, when given the number `p`, could have made statement 2 after hearing Samantha make statement 1. You may assume that `p` is the product of two numbers both greater than or equal to 2 and that Patricia would not have been able to determine the two numbers by looking at `p` alone.

Your third task is to write a function `statement3(s)` that takes an `int` argument `s` and returns `True` if and only if Samantha, when given the number `s`, could have made statement 3 after hearing Patricia make statement 2.

Finally, it is to you to figure out what two numbers Alice was thinking of. Since there are multiple solutions, you must write a function `is_solution(a, b)` that returns `True` if and only if `a` and `b` could have been two numbers that Alice was thinking of.

Hint: To get you started, think of what Samantha's first statement implies. Samantha knows that Patricia was not given the product of two primes. That means that the sum that Samantha was given cannot be written as the sum of two primes. Goldbach's conjecture stipulates that every even number greater than 3 can be written as the sum of two primes. Although Goldbach's conjecture has not yet been proven, you may assume that it has been verified for all numbers involved in the test cases here. So we know that the sum that Samantha was given must be odd. The only way to write an odd number as the sum of two primes is when one of the primes is 2, the only even prime. This means that the number given to Samantha is not the sum of 2 and a prime.

Difficulty: interview

Starter code:

def statement1(s):
	

Solutions:
def is_prime(n):
    return n == 2 or n % 2 != 0 and all(n % k != 0 for k in range(3, root(n) + 1, 2))

def root(p):
    return int(p ** 0.5)

def statement1(s):
    return not(s % 2 == 0 or is_prime(s - 2))

def statement2(p):
    return sum(statement1(i + p / i) for i in range(2, root(p) + 1) if p % i == 0) == 1

def statement3(s):
    return sum(statement2(i * (s - i)) for i in range(2, s / 2 + 1)) == 1

def is_solution(a, b):
    return statement1(a + b) and statement2(a * b) and statement3(a + b)


# two numbers >= 2
def statement1(s):
    #task: determine if
    #sum determines that numbers cannot be determined from product without other info
    return not prime(s-2) and not s%2 == 0 
    #conclusion made:
    #--> sum is odd --> two numbers are even,odd
    #--> sum not prime + 2 --> two numbers are not 2,prime
def statement2(p):
    #task: determine if
    #numbers can be determined from product after knowing that statement 1 is true
    l = [i for i in factor_pairs(p) if (i[0]>=2 and i[1] >=2)]
    l_ = [i for i in l if statement1(sum(i))]
    return l != l_ and len(l_) == 1
    #conclusion made:
    #--> statement one is true about exactly one factor pair's sum --> statement one
    #(cont) is true about only one combination of numbers that make the sum
def statement3(s):
    #task: determine if
    #numbers can be determined from sum after knowing that statement 2 is true
    return len([(i,s-i) for i in range(2,s//2+1) if statement2(i*(s-i))])==1
def is_solution(a, b):
    #task: determine if
    #statement 1,two,and 3 are true about the two numbers
    return statement1(a+b) and statement2(a*b) and statement3(a+b)
def prime(x):
    #determines if a number is prime
    x = int(x)
    for i in range(2,x):
        if x % i == 0:
            return False
    return True
def factor_pairs(x):
    res = set([])
    for i in range(1,x):
        if x%i == 0:
            res.add(tuple(sorted(([i,x/i]))))
    return sorted([[int(j) for j in i]for i in list(res)],key = lambda x: x[0])

from math import sqrt
is_prime    = lambda x: all(x%y!=0 for y in range(2,int(sqrt(x))+1))
statement1  = lambda s: s%2>0 and not is_prime(s-2)
statement2  = lambda p: 1 == sum(p%i==0 and statement1(i + p//i) for i in range(2,int(sqrt(p))+1))
statement3  = lambda s: 1 == sum(statement2(i * (s-i)) for i in range(2,s//2+1))
is_solution = lambda a,b: statement1(a+b) and statement2(a*b) and statement3(a+b)


from functools import reduce

def isPrime(n):
    d = 2

    while True:
        if n % d == 0:
            return False
        if d * d > n:
            return True
        d += 1

def primeFactor(n):
    d = 2
    factors = []
    while True:
        if n % d == 0:
            factors.append(d)
            n = n // d
        else:
            d += 1
        
        if n == 1:
            return factors
        elif d * d > n:
            return factors + [n]        

def get_cases(nums):
    nums_plus = list(map(lambda x : x + 1, nums))
    total_cases = reduce( lambda x, y: x * y, nums_plus)

    for i in range(total_cases):
        v = i
        cases = []
        for n in nums_plus:
            cases.append(v % n)
            v = v // n
        yield(cases)

def statement1(s):
    if s % 2 == 0:
        return False
    return not isPrime( s - 2)

def statement2(p):
    factors = primeFactor(p)
    
    if len(factors) == 2:
        return False

    factors_set = set(factors)
    factor_tuples = []
    for factor in factors_set:
        factor_tuples.append( (factor, factors.count(factor)) )
    
    sum_set = set()
    nums = [x[1] for x in factor_tuples]    
    for case in get_cases(nums):
        v1 = v2 = 1
        if sum(case) == 0 or sum(case) == len(factors):
            continue

        for i, n in enumerate(case):            
            v1 *= factor_tuples[i][0] ** (factor_tuples[i][1] - n)
            v2 *= factor_tuples[i][0] ** n            
        
        sum_set.add(v1 + v2)

    if sum([statement1(x) for x in sum_set]) == 1:
        return True
    return False

def statement3(s):
    if statement1(s) == False:
        return False
    
    if sum([statement2(x * (s - x)) for x in range(2, s // 2 + 1)]) == 1:
        return True
    return False

def is_solution(a, b):
    return statement3(a + b)

import math

def statement1(s):
    if s % 2 == 0: return False
    if is_prime(s - 2): return False
    return True

def statement2(p):
    if p % 2 != 0: return False
    facs = []
    for i in range(2,int(math.sqrt(p))+1):
        if p % i == 0:
            facs.append((i, int(p / i)))
    c = 0
    for f in facs:
        if statement1(f[0] + f[1]):
            c += 1
            if c > 1:
                return False
    return c == 1

def statement3(s):
    c = 0
    if not statement1(s): return False
    for i in range(2, s // 2):
        if statement2(i * (s - i)):
            c += 1
            if c > 1: return False
    return c == 1

def is_solution(a, b):
    return statement1(a + b) and statement2(a * b) and statement3(a + b)


def is_prime(n):
    if n < 2 or (n > 2 and n % 2 == 0):
        return False

    for i in range(3,int(math.sqrt(n))+1,2):
        if n % i== 0: 
            return False
    return True

def is_prime(n):
    if n==2:
        return True
    for i in range(2, int(n**.5)+1):
        if n%i==0: # no remainder
            return False
    return True
        
def factor(n):
    res = []
    for i in range(2, int(n**.5)+1):
        if n%i==0:
            res.append((i,n//i))
    return res

def statement1(s):
    # Statement 1: "Patricia cannot know what the two numbers are."
    # so sum is not sum of 2 primes, since goldbach conjecture says
    # all even numbers>3 can be written as sum of 2 primes
    # then sum is not even
    # if sum is even then statement 1 cannot be made
    if s%2==0: return False
    # so sum is odd, need to make sure that s-2 is not prime
    elif is_prime(s-2): return False
    # otherwise we are sure s cannot be written as sum of two primes  
    else: return True

def statement2(p):
    # Statement 2: "In that case, I do know what the two numbers are."
    # a,b not primes but patricia knows the numbers
    # we know that sam's number is odd and cannot be written as 2+prime
    # factor p into pairs of factors and sum the pairs up
    # if sum is even, remove. if sum can be written as 2+prime, remove.
    # if numbers in pair are both prime, remove
    # if left with only one pair -> True; else -> False
    factors = factor(p)
    count = 0
    for (f1, f2) in factors:
        tests = [(f1+f2)%2!=0, not is_prime(f1+f2-2), not (is_prime(f1) and is_prime(f2))]
        if all(tests): 
            count += 1
    return count==1

def statement3(s):
    # s needs to pass statement1 first
    # then, get all pairs of number that sum up to s
    # make sure that only 1 such pair passes statement2
    if not statement1(s): return False
    pairs = [(x, s-x) for x in range(2, s//2)]
    count = 0
    for p1, p2 in pairs:
        if statement2(p1*p2):
            count += 1
    return count==1

def is_solution(a, b):
    return all([statement1(a+b), statement2(a*b), statement3(a+b)])


def isPrime(n):
    return n is 2 or n % 2 is not 0 and all(n % i != 0 for i in range(3, int(n ** 0.5) + 1, 2))

def statement1(s):
    return not (s % 2 is 0 or isPrime(s - 2))

def statement2(p):
    return sum(statement1(i + int(p / i)) for i in range(2, int(p ** 0.5) + 1) if p % i == 0) is 1

def statement3(s):
    return sum(statement2(i * (s - i)) for i in range(2, int(s / 2) + 1)) is 1

def is_solution(a, b):
    return statement3(a + b)

def statement1(s):
    return not prime(s-2) and not s%2 == 0
def statement2(p):
    l = [i for i in factor_pairs(p) if (i[0]>=2 and i[1] >=2)]
    l_ = [i for i in l if statement1(sum(i))]
    return l != l_ and len(l_) == 1
def statement3(s):
    return len([(i,s-i) for i in range(2,s//2+1) if statement2(i*(s-i))])==1
def is_solution(a, b):
    return statement1(a+b) and statement2(a*b) and statement3(a+b)
def prime(x):
    for i in range(2,int(x)):
        if int(x) % i == 0:
            return False
    return True
def factor_pairs(x):
    res = set([])
    for i in range(1,x):
        if x%i == 0:
            res.add(tuple(sorted(([i,x/i]))))
    return sorted([[int(j) for j in i]for i in list(res)],key = lambda x: x[0])

import itertools

def statement1(s):
    return s%2 and not(is_prime(s-2))

def statement2(p):
    
    return len(get_posses(p)) == 1



def statement3(s):
    posses = ( [a,s-a] for a in range(s))
    posses = {tuple(sorted(i) ) for i in posses if min(i) >=2}
    
    finals = [ [a,b] for [a,b] in posses if statement2(a*b)]
    return len(finals) == 1

def is_solution(a, b):
    return statement1(a+b) and statement2(a*b) and statement3(a+b)
    
def get_posses(p):
    posses = [ (a,b)
    for a,b in ineff_poss_factors(p)
        if statement1(a+b)
        ]
    return posses
    
def is_prime(n):
    return len(list(prime_factors(n))) == 1
    
def ineff_poss_factors(n):
    for i in itertools.count(start = 2):
        if i**2 > n: break
        
        if not (n%i):
            yield (i, n//i)
    
def prime_factors(n):
    
    for i in itertools.count(start = 2):
        if i**2 > n: break
        if not n% i:
            yield i
            n//= i
    if n > 1:
        yield n

primes=[2,3,5,7,11,13,17,19,23,29,31]
for i in range(37,500,2):
    if all(i%j for j in primes): primes.append(i)

def statement1(s): return s%2 and s-2 not in primes

def statement2(p):
    possible_pairs=set()
    for i in range(2,int(p**.5)+2):
        if p%i==0:
            possible_pairs.add((i,p//i))
    good_pairs=[statement1(i+j) for i,j in possible_pairs]
    return sum(good_pairs)==1

def statement3(s):
    possible_pairs=set()
    for i in range(2,s//2+2):
        possible_pairs.add((i,s-i))
    good_pairs=[statement2(i*j) for i,j in possible_pairs]
    return sum(good_pairs)==1

def is_solution(a, b):
    return statement1(a+b) and statement2(a*b) and statement3(a+b)