\set DIGITS_BIGINT 19
\set DIGITS_MAX_SAFE_INTEGER 16

SELECT _id_template(
  $sql$
    DROP FUNCTION IF EXISTS {I:func_name}();

    CREATE OR REPLACE FUNCTION {I:func_name}(seq_name regclass = NULL) RETURNS bigint
    LANGUAGE plpgsql
    SET search_path FROM CURRENT
    AS $$
    -- Generates next globally-unique randomly-looking id, e.g.:
    --   EssssRRRRR...
    -- where decimal positions are:
    -- a) E is environment number (e.g. 1..8)
    -- b) s is microshard number (0..9999)
    -- c) R is random-looking part (e.g. up to 10^14 which is > 2^46)
    DECLARE
      seq regclass;
      plain_id bigint;
      id bigint;
    BEGIN
      seq := COALESCE(seq_name, (quote_ident({current_schema}) || '.id_seq')::regclass);
      plain_id := nextval(seq);
      IF plain_id < 0 OR plain_id >= {rnd_mul} THEN
        RAISE EXCEPTION 'Too many ids generated by %: % (max %)', seq, plain_id, {rnd_mul};
      END IF;
      id := {I:current_schema}.id_env_no();
      id := id * {CONST_SHARD_MUL} + {I:current_schema}.id_shard_no();
      id := id * {rnd_mul} + {I:current_schema}.id_pseudo_encrypt(
        {rnd_bits},
        plain_id,
        {CONST_MUL},
        {CONST_SUM},
        {CONST_MOD}
      );
      RETURN id;
    END
    $$;

    COMMENT ON FUNCTION {I:func_name}(regclass) IS
      'Generates next globally-unique randomly-looking id. The main idea is to '
      'not let external people infer the rate at which the ids are generated, '
      'even when they look at some ids sample.';
  $sql$,
  'CONST_SHARD_MUL', :'CONST_SHARD_MUL',
  'CONST_MUL', :'CONST_MUL',
  'CONST_SUM', :'CONST_SUM',
  'CONST_MOD', :'CONST_MOD',
  'func_name', func_name,
  'rnd_bits', (trunc(log(2, round(10::numeric ^ digits / :'CONST_ENV_MUL' / :'CONST_SHARD_MUL')))::integer >> 1 << 1)::text,
  'rnd_mul', round(10::numeric ^ digits / :'CONST_ENV_MUL' / :'CONST_SHARD_MUL')::text,
  'current_schema', current_schema
)
FROM (VALUES
  (:'DIGITS_BIGINT'::integer, 'id_gen'),
  (:'DIGITS_MAX_SAFE_INTEGER'::integer, 'id_gen_max_safe_integer')
) AS t(digits, func_name)
\gexec
