///|
pub(all) struct UnitScales {
  length : Double
  density : Double
  time : Double
  mass : Double
  charge : Double
  temperature : Double
  magnetic : Double
} derive(Debug, ToJson)

///|
pub fn UnitScales::si(
  length : Double,
  density : Double,
  time : Double,
) -> UnitScales {
  let safe_length = length.abs().max(1.0e-30)
  let safe_density = density.abs().max(1.0e-30)
  let safe_time = time.abs().max(1.0e-30)
  {
    length: safe_length,
    density: safe_density,
    time: safe_time,
    mass: electron_mass,
    charge: elementary_charge,
    temperature: 1.0,
    magnetic: 1.0,
  }
}

///|
pub fn normalize_length(scales : UnitScales, value : Double) -> Double {
  value / scales.length
}

///|
pub fn denormalize_length(scales : UnitScales, value : Double) -> Double {
  value * scales.length
}

///|
pub fn normalize_time(scales : UnitScales, value : Double) -> Double {
  value / scales.time
}

///|
pub fn denormalize_time(scales : UnitScales, value : Double) -> Double {
  value * scales.time
}

///|
pub fn normalize_density(scales : UnitScales, value : Double) -> Double {
  value / scales.density
}

///|
pub fn denormalize_density(scales : UnitScales, value : Double) -> Double {
  value * scales.density
}

///|
pub fn normalize_mass(scales : UnitScales, value : Double) -> Double {
  value / scales.mass
}

///|
pub fn denormalize_mass(scales : UnitScales, value : Double) -> Double {
  value * scales.mass
}

///|
pub fn normalize_charge(scales : UnitScales, value : Double) -> Double {
  value / scales.charge
}

///|
pub fn denormalize_charge(scales : UnitScales, value : Double) -> Double {
  value * scales.charge
}

///|
pub fn normalize_velocity(scales : UnitScales, value : Double) -> Double {
  value * scales.time / scales.length
}

///|
pub fn denormalize_velocity(scales : UnitScales, value : Double) -> Double {
  value * scales.length / scales.time
}

///|
pub fn normalize_temperature(scales : UnitScales, value : Double) -> Double {
  value / scales.temperature
}

///|
pub fn denormalize_temperature(scales : UnitScales, value : Double) -> Double {
  value * scales.temperature
}

///|
pub fn normalize_electric_field(scales : UnitScales, value : Double) -> Double {
  value *
  scales.charge *
  scales.length /
  (scales.mass * scales.length * scales.length / (scales.time * scales.time))
}

///|
pub fn denormalize_electric_field(
  scales : UnitScales,
  value : Double,
) -> Double {
  value * scales.mass / (scales.charge * scales.time * scales.time)
}

///|
pub fn thermal_unit(scales : UnitScales) -> Double {
  scales.temperature
}

///|
pub fn plasma_time_unit(
  scales : UnitScales,
  density : Double,
  mass : Double,
  charge : Double,
) -> Double {
  let frequency = plasma_frequency(density, charge_c=charge, mass_kg=mass)
  if frequency == 0.0 {
    scales.time
  } else {
    1.0 / frequency
  }
}

///|
pub fn scales_summary(scales : UnitScales) -> String {
  "length=\{scales.length}\ndensity=\{scales.density}\ntime=\{scales.time}\nmass=\{scales.mass}\ncharge=\{scales.charge}\n"
}