anchor.diff:      Avg = safe_average(Values),
       square_sum(Values, Avg, 0, 0).
   
  -square_sum([], Avg, Sum, Len) ->
  +square_sum([], _Avg, Sum, Len) ->
       Sum / Len;
   square_sum([{_TS, null} | Vs], Avg, Sum, Len) ->
       square_sum(Vs, Avg, Sum, Len);
  
positive.code:      ?_assertEqual(pseudo_series([1.5, 1.0], 30), consolidate(pseudo_series([1, null, 2, null, null, 1]), 3)),
       ?_assertEqual(pseudo_series([], 20), consolidate(pseudo_series([]), 2))
      ].
  
  percentile_test_() ->
      [?_assertEqual(null, percentile([], 50)),
  
negative.code:     end.
  
  
  normalize(SeriesLst) ->
      Series = lists:flatten(SeriesLst),
      {_Start, _End, Step} = normalize_stats(Series),
      % TODO: properly handle start/stop
      lists:map(fun(S) -> consolidate(S, Step div S#series.step) end, Series).
  
  
  normalize_stats([Hd | Tl]) ->
      Acc = {Hd#series.start, Hd#series.until, Hd#series.step},
      {S, End0, Step} = lists:foldr(fun(S, {Start0, End0, Step0}) ->
                                            Start = min(Start0, S#series.start),
                                            End = max(End0, S#series.until),
                                            Step = lcm(Step0, S#series.step),
                                            {Start, End, Step}
                                    end, Acc, Tl),
      End = (End0 - S) rem Step,
      {S, End, Step}.
  
  
  safe_minimum([]) -> null;
  safe_minimum(Values) ->
      safe_minimum(Values, null).
  