(******************************************************************************)
(* Copyright © Joly Clément, 2015                                        *)
(*                                                                            *)
(*  leowzukw@oclaunch.eu.org                                                  *)
(*                                                                            *)
(*  Ce logiciel est un programme informatique servant à exécuter              *)
(*  automatiquement des programmes à l'ouverture du terminal.                 *)
(*                                                                            *)
(*  Ce logiciel est régi par la licence CeCILL soumise au droit français et   *)
(*  respectant les principes de diffusion des logiciels libres. Vous pouvez   *)
(*  utiliser, modifier et/ou redistribuer ce programme sous les conditions    *)
(*  de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA    *)
(*  sur le site "http://www.cecill.info".                                     *)
(*                                                                            *)
(*  En contrepartie de l'accessibilité au code source et des droits de copie, *)
(*  de modification et de redistribution accordés par cette licence, il n'est *)
(*  offert aux utilisateurs qu'une garantie limitée.  Pour les mêmes raisons, *)
(*  seule une responsabilité restreinte pèse sur l'auteur du programme,  le   *)
(*  titulaire des droits patrimoniaux et les concédants successifs.           *)
(*                                                                            *)
(*  A cet égard  l'attention de l'utilisateur est attirée sur les risques     *)
(*  associés au chargement,  à l'utilisation,  à la modification et/ou au     *)
(*  développement et à la reproduction du logiciel par l'utilisateur étant    *)
(*  donné sa spécificité de logiciel libre, qui peut le rendre complexe à     *)
(*  manipuler et qui le réserve donc à des développeurs et des professionnels *)
(*  avertis possédant  des  connaissances  informatiques approfondies.  Les   *)
(*  utilisateurs sont donc invités à charger  et  tester  l'adéquation  du    *)
(*  logiciel à leurs besoins dans des conditions permettant d'assurer la      *)
(*  sécurité de leurs systèmes et ou de leurs données et, plus généralement,  *)
(*  à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.        *)
(*                                                                            *)
(*  Le fait que vous puissiez accéder à cet en-tête signifie que vous avez    *)
(*  pris connaissance de la licence CeCILL, et que vous en avez accepté les   *)
(*  termes.                                                                   *)
(******************************************************************************)

open Core.Std
open Core_bench.Std

(* File to test who is the faster to swap two elements *)
(* TODO:
  * Use core_bench for trustable result
  * Improve list algo ?
  * Verify that output of all functions are the same *)

(* Arguments:
  * li: a list of elements
  * a,b: positions
  * a < b*)

let data =  List.init 5 ~f:(fun i -> i)
let big_data = List.init 1_000_000 ~f:(fun i -> i)

(* With an array *)
let array li a b =
  let ar = List.to_array li in
  (* Store a value *)
  let v = ar.(a) in
  ar.(a) <- ar.(b);
  ar.(b) <- v;
  Array.to_list ar
;;

(* Manipulating list *)
let list li a b =
  (* We need a < b *)
  let ( a, b ) : ( int * int ) =
    if a < b
    then ( a, b )
    else ( b, a )
  in
  let b' = List.nth_exn li b in (* Ephemeral value, to swap *)
  let a' = ref b' in (* Avoid walk around the list twice *)
  List.mapi li
    ~f:(fun i element ->
      if i = a
      then begin
        a' := element;
        b'
      end
      else if i = b
      then !a'
      else
        element
      )
;;

(* Another idee *)
let once li a b =
  let rec worker li head middle tail ~ca ~cb ~i =
    match ( ca, cb ) with
    | ( Some ca ), ( Some cb ) -> head @ [cb] @ middle @ [ca] @ tail
    | _ -> li |> (function
      | hd :: tl ->
        if i < a
        then worker tl (head @ [hd]) middle tail ~ca ~cb ~i:(succ i)
        else if i = a
        then worker tl head middle tail ~ca:(Some hd) ~cb ~i:(succ i)
        else if i < b
        then worker tl head (middle @ [hd]) tail ~ca ~cb ~i:(succ i)
        else if i = b
        then worker ~ca ~cb:(Some hd) tl ~i:(succ i) head middle tail
        else if i > b
        then worker [] head middle li ~ca ~cb ~i:(-1)
        else assert false
      | [] -> []) (* both a and b are set, should be taken before *)
  in
  let ( a, b ) = if a < b then (a,b) else (b,a) in
  worker li [] [] [] ~ca:None ~cb:None ~i:0
;;


(* Element to swap *)
let a' = 2;;
let b' = 4;;
let a'' = 0;;
let b'' = 10_000 - 1;;

(* Visual test *)
let print_li li =
  printf "[ ";
  List.iter li ~f:(fun e -> printf "%i; " e);
  printf "]\n"
;;

print_li (array data a' b');;
print_li (list data a' b');;
print_li (once data a' b');;

let tests = [
  "Array - small list", (fun () -> array data a' b');
  "Array - big list", (fun () -> array big_data a'' b'');
  "List - small list", (fun () -> list data a' b');
  "List - big list", (fun () -> list big_data a'' b'');
  "Once - small list", (fun () -> once data a' b');
  "Once - big once", (fun () -> once big_data a'' b'');
]

let () =
  List.map tests ~f:(fun (name,test) -> Bench.Test.create ~name test)
  |> Bench.make_command
  |> Command.run