package fj.demo.euler;
import fj.P1;
import static fj.data.Enumerator.naturalEnumerator;
import fj.data.Natural;
import static fj.data.Natural.ZERO;
import static fj.data.Natural.natural;
import fj.data.Stream;
import static fj.data.Stream.*;
import fj.data.vector.V2;
import static fj.Ord.naturalOrd;
import static fj.Show.naturalShow;
/**
* Find the largest prime factor of a composite number.
*/
public class Problem3 {
// An infinite stream of all the primes.
public static final Stream<Natural> primes = cons(natural(2).some(), () -> forever(naturalEnumerator, natural(3).some(), 2).filter(n -> primeFactors(n).length() == 1));
//Finds factors of a given number.
public static Stream<Natural> factor(final Natural n, final Natural p, final P1<Stream<Natural>> ps) {
Stream<Natural> ns = cons(p, ps);
Stream<Natural> ret = nil();
while (ns.isNotEmpty() && ret.isEmpty()) {
final Natural h = ns.head();
final P1<Stream<Natural>> t = ns.tail();
if (naturalOrd.isGreaterThan(h.multiply(h), n))
ret = single(n);
else {
final V2<Natural> dm = n.divmod(h);
if (naturalOrd.eq(dm._2(), ZERO))
ret = cons(h, () -> factor(dm._1(), h, t));
else ns = ns.tail()._1();
}
}
return ret;
}
// Finds the prime factors of a given number.
public static Stream<Natural> primeFactors(final Natural n) {return factor(n, natural(2).some(), primes.tail());}
public static void main(final String[] args) {
naturalShow.println(primeFactors(natural(600851475143L).some()).last());
}
}