-module(db_test).

-export([scan_vals/2,dict/2,orddict/2,rbdict/2,gb_trees/2]).
-export([rt/3]).

-import(lists, [reverse/2,seq/2,seq/3,sort/1]).

-define(KEY(N), N).
%%-define(KEY(N), {foo,bar,N}).

-define(RLOOP(F1, F2),
	F1(Reps, Ks, D) when Reps > 1 ->
	       F2(Ks, D),
	       F1(Reps-1, Ks, D);
	F1(1, Ks, D) -> F2(Ks, D)).

-define(STORE(F1, F2),
	F1([K|Ks], D0) ->
	       D1 = F2(?KEY(K), stuff, D0),
	       F1(Ks, D1);
	F1([], D) -> D).

-define(FETCH(F1, F2),
	F1([K|Ks], D) ->
	       F2(?KEY(K), D),
	       F1(Ks, D);
	F1([], D) -> D).

scan_vals(0, N) ->
    [ X || {_,X} <- sort([ {random:uniform(N),X} || X <- seq(1, N) ]) ];
scan_vals(1, N) -> seq(1, N);			%Straight sweep
scan_vals(2, N) ->				%Middle out
    M = N div 2,
    interlace(seq(M, 1, -1), seq(M+1, N, 1));		  
scan_vals(3, N) ->				%Half and half
    M = N div 2,
    interlace(seq(1, M, 1), seq(M+1, N, 1));
scan_vals(4, N) ->				%Double sweep
    seq(1, N, 2) ++ seq(2, N, 2).

?RLOOP(null, null1).
?FETCH(null1, nullop).

nullop(_, _) -> ok.

interlace([H1|T1], [H2|T2]) -> [H1,H2|interlace(T1, T2)];
interlace([], L2) -> L2;
interlace(L1, []) -> L1.

dict(Reps, Ks) ->
    %% Initial setup
    D0 = dict:new(),
    Nreps = (10000000 div length(Ks)) + 1,	%Long timing
    {_,_} = statistics(runtime),
    %% Null sweep
    null(Nreps, Ks, D0),
    {_,Tn} = statistics(runtime),
    %% Initial store
    D1 = d_sto(Reps, Ks, D0),
    {_,Ti} = statistics(runtime),
    %% Fetching values
    D2 = d_fet(Reps, Ks, D1),
    {_,Tf} = statistics(runtime),
    %% Updating table
    D3 = d_sto(Reps, Ks, D1),
    {_,Tu} = statistics(runtime),
    {Tn/Nreps,Ti/Reps,Tf/Reps,Tu/Reps}.

?RLOOP(d_sto, d_sto1).
?STORE(d_sto1, dict:store).

?RLOOP(d_fet, d_fet1).
?FETCH(d_fet1, dict:fetch).

orddict(Reps, Ks) ->
    D0 = orddict:new(),
    {_,_} = statistics(runtime),
    %% Null sweep
    null(Reps, Ks, D0),
    {_,Tn} = statistics(runtime),
    %% Initial store
    D1 = o_sto(Reps, Ks, D0),
    {_,Ti} = statistics(runtime),
    %% Fetching values
    D2 = o_fet(Reps, Ks, D1),
    {_,Tf} = statistics(runtime),
    %% Updating table
    D3 = o_sto(Reps, Ks, D1),
    {_,Tu} = statistics(runtime),
    {Tn/Reps,Ti/Reps,Tf/Reps,Tu/Reps}.

?RLOOP(o_sto, o_sto1).
?STORE(o_sto1, orddict:store).

?RLOOP(o_fet, o_fet1).
?FETCH(o_fet1, orddict:fetch).

rbdict(Reps, Ks) ->
    D0 = rbdict:new(),
    {_,_} = statistics(runtime),
    %% Null sweep
    null(Reps, Ks, D0),
    {_,Tn} = statistics(runtime),
    %% Initial store
    D1 = r_sto(Reps, Ks, D0),
    {_,Ti} = statistics(runtime),
    %% Fetching values
    D2 = r_fet(Reps, Ks, D1),
    {_,Tf} = statistics(runtime),
    %% Updating table
    D3 = r_sto(Reps, Ks, D1),
    {_,Tu} = statistics(runtime),
    {Tn/Reps,Ti/Reps,Tf/Reps,Tu/Reps}.

?RLOOP(r_sto, r_sto1).
?STORE(r_sto1, rbdict:store).

?RLOOP(r_fet, r_fet1).
?FETCH(r_fet1, rbdict:fetch).

gb_trees(Reps, Ks) ->
    D0 = gb_trees:empty(),
    {_,_} = statistics(runtime),
    %% Null sweep
    null(Reps, Ks, D0),
    {_,Tn} = statistics(runtime),
    %% Initial store
    D1 = g_ins(Reps, Ks, D0),
    {_,Ti} = statistics(runtime),
    D2 = g_ent(Reps, Ks, D0),
    {_,Te1} = statistics(runtime),
    %% Fetching values
    D3 = g_get(Reps, Ks, D1),
    {_,Tf} = statistics(runtime),
    %% Updating table
    D4 = g_upd(Reps, Ks, D1),
    {_,Tu} = statistics(runtime),
    D5 = g_ent(Reps, Ks, D1),
    {_,Te2} = statistics(runtime),
    {Tn/Reps,Ti/Reps,Te1/Reps,Tf/Reps,Tu/Reps,Te2/Reps}.

?RLOOP(g_ins, g_ins1).
?STORE(g_ins1, gb_trees:insert).

?RLOOP(g_ent, g_ent1).
?STORE(g_ent1, gb_trees:enter).

?RLOOP(g_upd, g_upd1).
?STORE(g_upd1, gb_trees:update).

?RLOOP(g_get, g_get1).
?FETCH(g_get1, gb_trees:get).

rt(C, Reps, N) ->
    {Rds,Rrs,Rgs} = rt1(C, Reps, N),
    Fd = fun ({Null,Sto,Fet,Upd}, {Nulla,Stoa,Feta,Upda}) ->
		 {Null+Nulla,Sto+Stoa,Fet+Feta,Upd+Upda}
	 end,
    Fr = fun ({Null,Sto,Fet,Upd}, {Nulla,Stoa,Feta,Upda}) ->
		 {Null+Nulla,Sto+Stoa,Fet+Feta,Upd+Upda}
	 end,
    Fg = fun ({Null,Ins,Ent1,Fet,Upd,Ent2}, {Nulla,Insa,Ent1a,Feta,Upda,Ent2a}) ->
		 {Null+Nulla,Ins+Insa,Ent1+Ent1a,Fet+Feta,Upd+Upda,Ent2+Ent2a}
	 end,
    Rd = lists:foldl(Fd, {0,0,0,0}, Rds),
    Rr = lists:foldl(Fr, {0,0,0,0}, Rrs),
    Rg = lists:foldl(Fg, {0,0,0,0,0,0}, Rgs),
    {Rds,avd(C, Rd, N),Rrs,avr(C, Rr, N),Rgs,avg(C, Rg, N)}.

rt1(C, Reps, N) when C > 0 ->
    L = scan_vals(0, N),
    {Rd,Rr,Rg} = rt1(Reps, L),
    {Rds,Rrs,Rgs} = rt1(C-1, Reps, N),
    {[Rd|Rds],[Rr|Rrs],[Rg|Rgs]};
rt1(0, _, _) -> {[],[],[]}.

rt1(Reps, L) ->
    Rd = dict(Reps, L),
    Rr = rbdict(Reps, L),
    Rg = gb_trees(Reps, L),
    {Rd,Rr,Rg}.

avd(C, {Null,S,F,U}, N) ->
    {(S-Null)/C,(F-Null)/C,(U-Null)/C}.

avr(C, {Null,S,F,U}, N) ->
    {(S-Null)/C,(F-Null)/C,(U-Null)/C}.

avg(C, {Null,I,E1,F,U,E2}, N) ->
    {(I-Null)/C,(E1-Null)/C,(F-Null)/C,(U-Null)/C,(E2-Null)/C}.
