\pset pager off
CREATE OR REPLACE FUNCTION test_div_perf(IN ddig1 int,
                                         IN ddig2 int,
                                         IN rs int,
                                         OUT ndig1 int,
                                         OUT ndig2 int,
                                         OUT rscale int,
                                         OUT div_knuth float4,
                                         OUT new_exact float4,
                                         OUT div_fast float4,
                                         OUT new_inexact float4)
AS
$$
DECLARE
  min1 numeric;
  min2 numeric;
  max1 numeric;
  max2 numeric;
  x numeric;
  y numeric;
  z1 numeric;
  z2 numeric;
  z3 numeric;
  z4 numeric;
  nreps int;
  ts_start timestamptz;
  ts_end timestamptz;
  nrows int;
  secs float4;
BEGIN
  ndig1 := (ddig1 + 3) / 4;
  ndig2 := (ddig2 + 3) / 4;
  rscale := rs;

  min1 := ('1e'||(ddig1 - 1))::numeric;
  min2 := ('1e'||(ddig2 - 1))::numeric;
  max1 := ('1e'||ddig1)::numeric - 1;
  max2 := ('1e'||ddig2)::numeric - 1;

  PERFORM setseed(0);
  x := random(min1, max1);
  y := random(min2, max2);

  nreps := 0;
  ts_start := clock_timestamp();
  ts_end := ts_start + '2 sec'::interval;
  LOOP
    z1 := div_knuth(x, y, rscale, true);
    nreps := nreps + 1;
    EXIT WHEN clock_timestamp() > ts_end;
  END LOOP;

  ts_start := clock_timestamp();
  FOR i IN 1..nreps LOOP
    ts_end := clock_timestamp();
  END LOOP;

  nreps := nreps * 5.0 / (2 - least(extract(epoch from (ts_end - ts_start)), 0.5));
  nrows := (sqrt(nreps::numeric) + 0.5)::int;
  nreps := nrows * nrows;

  PERFORM setseed(0);
  ts_start := clock_timestamp();
  z1 := (SELECT sum(div_knuth(t1.x, t2.x, rscale, true))
           FROM (SELECT random(min1, max1)
                   FROM generate_series(1, nrows)) t1(x),
                (SELECT random(min2, max2)
                   FROM generate_series(1, nrows)) t2(x));
  ts_end := clock_timestamp();
  secs := extract(epoch from (ts_end - ts_start));
  div_knuth := nreps / secs;
  RAISE NOTICE 'div_knuth(%/% to %): %', ndig1, ndig2, rscale, div_knuth;

  PERFORM setseed(0);
  ts_start := clock_timestamp();
  z2 := (SELECT sum(div_new(t1.x, t2.x, rscale, true, true))
           FROM (SELECT random(min1, max1)
                   FROM generate_series(1, nrows)) t1(x),
                (SELECT random(min2, max2)
                   FROM generate_series(1, nrows)) t2(x));
  ts_end := clock_timestamp();
  secs := extract(epoch from (ts_end - ts_start));
  new_exact := nreps / secs;
  RAISE NOTICE 'new_exact(%/% to %):  %', ndig1, ndig2, rscale, new_exact;
  IF z2 != z1 THEN
    RAISE NOTICE '  error: %', z2 - z1;
  END IF;

  PERFORM setseed(0);
  ts_start := clock_timestamp();
  z3 := (SELECT sum(div_fast(t1.x, t2.x, rscale, true))
           FROM (SELECT random(min1, max1)
                   FROM generate_series(1, nrows)) t1(x),
                (SELECT random(min2, max2)
                   FROM generate_series(1, nrows)) t2(x));
  ts_end := clock_timestamp();
  secs := extract(epoch from (ts_end - ts_start));
  div_fast := nreps / secs;
  RAISE NOTICE 'div_fast(%/% to %): %', ndig1, ndig2, rscale, div_fast;
  IF z3 != z1 THEN
    RAISE NOTICE '  error: %', z3 - z1;
  END IF;

  PERFORM setseed(0);
  ts_start := clock_timestamp();
  z4 := (SELECT sum(div_new(t1.x, t2.x, rscale, true, false))
           FROM (SELECT random(min1, max1)
                   FROM generate_series(1, nrows)) t1(x),
                (SELECT random(min2, max2)
                   FROM generate_series(1, nrows)) t2(x));
  ts_end := clock_timestamp();
  secs := extract(epoch from (ts_end - ts_start));
  new_inexact := nreps / secs;
  RAISE NOTICE 'new_inexact(%/% to %):  %', ndig1, ndig2, rscale, new_inexact;
  IF z4 != z1 THEN
    RAISE NOTICE '  error: %', z4 - z1;
  END IF;
END;
$$ LANGUAGE plpgsql;

WITH t1(ndig1, ndig2, rs) AS (
  VALUES (1, 1, 20), (2, 2, 20), (3, 3, 20), (4, 4, 20), (5, 5, 20),
         (6, 6, 20), (7, 7, 20), (8, 8, 20), (9, 9, 20), (10, 10, 20),
         (12, 12, 20), (16, 16, 20), (20, 20, 20), (30, 30, 20),
         (40, 40, 20), (50, 50, 20), (60, 60, 20), (70, 70, 20),
         (80, 80, 20), (90, 90, 20), (100, 100, 20),
         (200, 200, 20), (300, 300, 20), (400, 400, 20), (500, 500, 20),
         (600, 600, 20), (700, 700, 20), (800, 800, 20), (1000, 1000, 20),
         (2000, 2000, 20), (3000, 3000, 20), (4000, 4000, 20),
         (5000, 5000, 20), (6000, 6000, 20), (7000, 7000, 20),
         (8000, 8000, 20), (10000, 10000, 20), (20000, 20000, 20),
         (30000, 30000, 20), (30000, 30000, 1000),
         (2, 1, 0), (4, 2, 0), (6, 3, 0), (8, 4, 0), (10, 5, 0),
         (12, 6, 0), (14, 7, 0), (16, 8, 0), (18, 9, 0), (20, 10, 0),
         (24, 12, 0), (32, 16, 0), (40, 20, 0), (60, 30, 0), (80, 40, 0),
         (100, 50, 0), (120, 60, 0), (140, 70, 0), (160, 80, 0),
         (180, 90, 0), (200, 100, 0), (400, 200, 0), (600, 300, 0),
         (800, 400, 0), (1000, 500, 0), (1200, 600, 0), (1400, 700, 0),
         (1600, 800, 0), (2000, 1000, 0), (4000, 2000, 0), (6000, 3000, 0),
         (8000, 4000, 0), (10000, 5000, 0), (12000, 6000, 0),
         (16000, 8000, 0), (20000, 10000, 0), (30000, 15000, 0),
         (30000, 1, 1000), (30000, 2, 1000), (30000, 3, 1000),
         (30000, 4, 1000), (30000, 5, 1000), (30000, 6, 1000),
         (30000, 7, 1000), (30000, 8, 1000), (30000, 9, 1000),
         (30000, 10, 1000), (30000, 12, 1000), (30000, 16, 1000),
         (30000, 20, 1000), (30000, 30, 1000), (30000, 40, 1000),
         (30000, 50, 1000), (30000, 60, 1000), (30000, 70, 1000),
         (30000, 80, 1000), (30000, 90, 1000), (30000, 100, 1000),
         (30000, 200, 1000), (30000, 300, 1000), (30000, 400, 1000),
         (30000, 500, 1000), (30000, 600, 1000), (30000, 700, 1000),
         (30000, 800, 1000), (30000, 1000, 1000), (30000, 2000, 1000),
         (30000, 3000, 1000), (30000, 4000, 1000), (30000, 5000, 1000),
         (30000, 6000, 1000), (30000, 8000, 1000), (30000, 10000, 1000),
         (30000, 15000, 1000), (30000, 20000, 1000), (30000, 25000, 1000),
         (30000, 30000, 1000)
), t2(result) AS (
  SELECT test_div_perf(ndig1 * 4, ndig2 * 4, rs) FROM t1
)
SELECT (result).* FROM t2;
