File: mathlib.ml

package info (click to toggle)
ocaml-batteries 3.10.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 4,104 kB
  • sloc: ml: 44,518; sh: 146; makefile: 96; ansic: 30; lisp: 15
file content (126 lines) | stat: -rw-r--r-- 4,247 bytes parent folder | download | duplicates (7)
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
open Batteries

let rec factorial = function 1 -> 1 | n -> n * factorial (n-1)

let rec big_factorial acc = function
  | 1 -> acc
  | n -> big_factorial (Big_int.mult_int_big_int n acc) (n-1)

let big_factorial n = big_factorial Big_int.unit_big_int n

let factors i f x =
  let acc = ref i in (* already counted 1 *)
  let max_test = x |> float |> sqrt |> Float.to_int in
  for i = 2 to max_test-1 do
    if x mod i = 0 then acc := f (x/i) (f i !acc)
  done;
  if x mod max_test = 0 && max_test <> 1
  then
    if max_test * max_test = x
    then f max_test !acc (* square - don't double count *)
    else f (x/max_test) (f max_test !acc)
  else !acc

let list_factors n = factors [1] List.cons n
let sum_factors n = factors 1 (+) n

(* list of small primes *)
let easy = [2; 3; 5; 7]
(* let patt = [2; 4; 2; 4; 6; 2; 6; 4; 2; 4; 6; 6; 2; 6; 4; 2; 6; 4; 6; 8; 4; 2;
 4; 2; 4; 8; 6; 4; 6; 2; 4; 6; 2; 6; 6; 4; 2; 4; 6; 2; 6; 4; 2; 4; 2; 10; 2; 10]
 *)


(* The following code builds a pattern list of skip sizes that can be
   used when searching for primes.

   The idea is an extension of checking only odd numbers (greater than
   2) for primality.  Knowing that [2;3;5] are prime, we can check [x;
   x+4; x+6; x+10; x+12; x+16; x+22; x+24; x+30] for primality (given
   an appropriate start value x), as we can know ahead of time that
   [x+1; x+2; x+3; x+5; x+7; ...] are composite by the same reasoning
   that we know that [x+1; x+3; x+5] are composite for odd [x].

   Instead of storing the offsets from [x], we store the offset from
   the previous, so the sequence above is represented by the pattern
   [4;2;4;2;4;6;2;6].

   We derive this sequence from a list of [n] small primes by first
   inspecting integers 2--10^n for being relatively prime to our small
   primes.  We then take the differences between subsequent values,
   and search for a periodic pattern in the results.  The smallest
   sequence that repeats is our desired testing pattern.


 *)

(* Find relative primes *)
let primes easy =
  let pattsearch_max = List.fold_left (fun a _ -> a * 10) 1 easy in
  let filter l n = Enum.filter (fun i -> i mod n > 0) l in
  List.fold_left filter (2--pattsearch_max) easy |> List.of_enum

(* compute successive differences *)
let diffs list =
  let rec loop acc = function
      a :: b :: t -> loop ((b-a)::acc) (b::t)
    | _ -> List.rev acc
  in
  loop [] list

(* test whether the elements of l1 and l2 are the same, ignoring any extra elements in one list *)
let rec list_eq_head l1 l2 = match l1,l2 with
    | h1::t1, h2::t2 -> h1 == h2 && list_eq_head t1 t2
    | [],[] | [],_ | _,[] -> true

(* test whether the sublist [sub] repeats to form the rest of [l] *)
let rec is_rep sub n lst =
  try
    let head,rest = List.split_nth n lst in
    head = sub && is_rep sub n rest
  with Invalid_argument _ -> list_eq_head lst sub

(* given a list, find the smallest sublist that repeats to create that list *)
let find_sub lst =
  let half_len = List.length lst / 2 in
  let rec loop n =
    if n > half_len then
      failwith "No repeating subsequence found, need more test primes";
    let sub = List.take n lst in
    if is_rep sub n lst then sub else loop (n+1)
  in
  loop 1

(* combines the above routines to build a primality testing skip pattern *)
let patt, patt_init =
  let ps = primes easy in
  let patt = diffs ps |> find_sub in
  patt, List.hd ps


open Return (* use batReturn for flow control *)

let test_sequence () = Enum.scanl (+) patt_init (List.enum patt |> Enum.cycle)

(* factor [comp], calling [found] on each prime factor *)
let factor found n =
  (* factor out as many factors of [t] from [n] *)
  label (fun label ->
    let rec test n t =
      (* exit from infinite fold here *)
      if t * t > n then (if n > t then found n; return label ())
      else if n mod t = 0 then
	let quot = n / t in (found t; test quot t)
      else n
    in
    (* test/reduce small primes *)
    let n = List.fold_left test n easy in
    (* infinite fold for rest of divisors, no return value *)
    test_sequence () |> Enum.fold test n |> ignore
  )

let factors n =
  let ret = RefList.empty () in
  factor (RefList.push ret) n;
  assert (RefList.fold_left ( * ) 1 ret = n);
  List.rev (RefList.to_list ret)