(* Algorithme de dessin de chryzodes *)
open Big_int;;

(* Pi *)
let pi = 4. *. atan 1.

(* Chryzodes: renvoie des couples de points du cercle *)
let chryzodes puissance npoints =
	let rec chryzodes points dejafait n pt =
		if List.mem pt dejafait then points
		else
			let next = int_of_big_int (mod_big_int (power_int_positive_int puissance n) (big_int_of_int npoints)) in
			chryzodes ((pt, next) :: points) (pt :: dejafait) (n + 1) next
	in
	let rec p = function
	|	n when truncate(float puissance ** float n) > npoints -> n
	|	n -> p (n + 1)
	in
	let li = chryzodes [] [] (p 0) puissance in List.tl (List.rev li)

(* Dessin des chryzodes à partir d'une liste de coordonnées de points *)
let rec draw ctx npoints coords = function
|	[] -> ()
|	(pt1, pt2) :: tl ->
		let x1, y1 = coords.(pt1) in
		let x2, y2 = coords.(pt2) in
		Cairo.move_to ctx x1 y1;
		Cairo.line_to ctx x2 y2;
		Cairo.close_path ctx;
		Cairo.stroke ctx;
		draw ctx npoints coords tl

(* Génère les coordonnées des points *)
let gencoords npoints rayon = Array.init npoints 
	(fun n -> cos (float n *. (2. *. pi /. float npoints)) *. rayon +. rayon,
	          sin (float n *. (2. *. pi /. float npoints)) *. rayon +. rayon)

(* Point d'entrée *)
let _ =
	(* Prise d'informations *)
	let size, puissance, npoints = Scanf.scanf "%d %d %d" (fun a b c -> a, b, c) in

	(* Initialisation de Cairo *)
	let surface = Cairo.image_surface_create Cairo.FORMAT_ARGB32 ~width:size ~height:size in
	let ctx = Cairo.create surface in
	Cairo.set_line_width ctx 2.;

	(* Cercle *)
	let beuh = (float size /. 2.) in
	Cairo.arc ctx ~xc:beuh ~yc:beuh ~radius:beuh ~angle1:0. ~angle2:(2. *. pi);

	(* Dessin *)
	Cairo.set_operator ctx Cairo.OPERATOR_XOR;
	draw ctx npoints (gencoords npoints (float size /. 2.)) (chryzodes puissance npoints);
	Cairo.close_path ctx;
	Cairo.stroke ctx;

	(* Sortie *)
	Cairo_png.surface_write_to_file surface "chryzodes.png"