forked from Anniepoo/prolog-examples
-
Notifications
You must be signed in to change notification settings - Fork 0
/
charmichael.pl
112 lines (92 loc) · 2.71 KB
/
charmichael.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
:- module(charmichael, [charmichael/2]).
/*
An integer n is a Carmichael number iff:
n is square-free
for every prime divisor p of n:
p - 1 | n - 1
*/
head([X | Y], X).
tail([X | Y], Y).
tail([X | []], []). % this is technically correct anyway
% divisibility function
divides(P, Q) :-
P mod Q =:= 0.
% finds a factorization of N (except 1 and N)
factorization(N, L) :-
factorization_prime(N, 2, L), !.
factorization_prime(N, M, L) :-
N_sqrt is sqrt(N),
N_sqrt < M,
L = []. % N is prime
factorization_prime(N, M, L) :-
N_sqrt is sqrt(N),
N_sqrt >= M,
R is N rem M,
R == 0,
D is N div M,
L = [M, D].
factorization_prime(N, M, L) :-
M_prime is M + 1,
factorization_prime(N, M_prime, L).
% computes the prime factorization of N
primeFactorization(N, Q) :-
primeFactorization_prime(N, [N], [], Q).
primeFactorization_prime(_N, [], P, Q) :-
Q = P.
primeFactorization_prime(N, [FH |FT], P, Q) :-
factorization(FH, F_factors),
pfp_helper([FH|FT], F_factors, N, P, P_prime, F_prime),
primeFactorization_prime(N, F_prime, P_prime, Q).
% prime factor case
pfp_helper([F_head | F_tail], [], N, P, P_prime, _) :-
F_head \= N, % or else we end up putting N in P if N is prime
append([F_head], P, P_prime),
F_prime = F_tail.
pfp_helper([_ | F_tail], F_factors, _N, P, P_prime, F_prime) :-
F_factors = [_ | _],
append(F_factors, F_tail, F_prime),
P_prime = P.
% check that p - 1 | n - 1
checkPrimeFactors(N) :-
primeFactorization(N, P),
checkPrimeFactors_prime(N, P).
checkPrimeFactors_prime(N, []).
checkPrimeFactors_prime(N, P) :-
head(P, P_head),
tail(P, P_tail),
N_prime is N - 1,
P_prime is P_head - 1,
N_prime > 0,
P_prime > 0,
divides(N_prime, P_prime),
checkPrimeFactors_prime(N, P_tail).
% determines whether a number is square-free
squareFree(N) :-
squareFree_prime(N, 2).
squareFree_prime(N, M) :- % our square is bigger than N - no square factor
M_square is M * M,
M_square > N.
squareFree_prime(N, M) :- % our square doesn't divide N - no square factor
M_square is M * M,
M_square =< N,
M_prime is M + 1,
not(divides(N, M_square)),
squareFree_prime(N, M_prime).
% generates all carmichael numbers up to N, inclusive
carmichael(N, L) :-
carmichael_prime(N, 1, [], Q),
L = Q.
carmichael_prime(N, M, P, Q) :-
N < M,
Q = P.
carmichael_prime(N, M, P, Q) :-
M_prime is M + 1,
checkCarmichael(M),
append([M], P, P_prime),
carmichael_prime(N, M_prime, P_prime, Q).
carmichael_prime(N, M, P, Q) :-
M_prime is M + 1,
carmichael_prime(N, M_prime, P, Q).
checkCarmichael(N) :-
squareFree(N),
checkPrimeFactors(N).