#!/usr/bin/env ruby
# frozen_string_literal: true

SAMPLES            = 4096
INT32_MAX          = 2**32
SEED               = 33
BYTES_PER_LINE    = 16
FLOATS_PER_LINE   = 8
INT32S_PER_LINE   = 8
INDENT = '    '

# Formats an array as a multi-line Rust array literal.
# If a formatter is provided, each element is formatted using it.
def format_rust_array(array, indent: INDENT, elements_per_line: nil, formatter: nil)
  if elements_per_line.nil? || elements_per_line <= 0
    raise ArgumentError,
          'elements_per_line must be a positive integer'
  end

  lines = array.each_slice(elements_per_line).map do |slice|
    # Format each element with the given formatter (or use inspect by default)
    formatted_elements = slice.map { |elem| formatter ? formatter.call(elem) : elem.inspect }.join(', ')
    formatted_elements
  end
  "[\n#{lines.map { |line| "#{indent}#{line}" }.join(",\n")},\n]"
end

# Generates reproducible data by creating two Random instances with the same seed.
# It verifies that both invocations yield the same result.
def generate_reproducible(seed)
  first  = yield(Random.new(seed))
  second = yield(Random.new(seed))
  raise 'not reproducible' unless first == second

  first
end

# Writes the generated vectors into a Rust source file.
def write_vectors(bytes, floats, int32s)
  # Format bytes: each number is printed in a 3-character field.
  formatted_bytes = format_rust_array(bytes.bytes, elements_per_line: BYTES_PER_LINE, formatter: lambda { |n|
    format('%3d', n)
  })

  # Format floats: each number is printed in a 20-character field.
  formatted_floats = format_rust_array(floats, elements_per_line: FLOATS_PER_LINE, formatter: lambda { |s|
    format('%20s', s)
  })

  # Format int32s: each number is printed in a 10-character field.
  formatted_int32s = format_rust_array(int32s, elements_per_line: INT32S_PER_LINE, formatter: lambda { |n|
    format('%10d', n)
  })

  output = <<~RUST
    // This file is automatically generated.
    //
    // See `scripts/reproduciblity.rb` if you would like to make changes to these vectors.

    #[rustfmt::skip]
    pub static BYTES_SEED_32: &[u8] = &#{formatted_bytes};

    #[rustfmt::skip]
    pub static REAL_SEED_32: &[f64] = &#{formatted_floats};

    #[rustfmt::skip]
    pub static INT32_SEED_32: &[u32] = &#{formatted_int32s};
  RUST

  file_path = File.join(__dir__, '..', 'tests', 'vectors', 'mod.rs')
  File.write(file_path, output)
end

if __FILE__ == $PROGRAM_NAME
  # Generate reproducible random sequences.
  bytes_a  = generate_reproducible(SEED) { |r| r.bytes(SAMPLES) }
  floats_a = generate_reproducible(SEED) { |r| SAMPLES.times.map { r.rand } }
  int32s_a = generate_reproducible(SEED) { |r| SAMPLES.times.map { r.rand(INT32_MAX) } }

  # Write the generated data to the Rust file.
  write_vectors(bytes_a, floats_a, int32s_a)
end
