import math from .agri_seed_rate_types import SeedRate from .math_round_div import round_div from .math_round_float import round_float def seed_rate(target_plants_per_square_metre: int, thousand_grain_weight_grams: float, establishment_basis_points: int) -> SeedRate: """Seed rate (kg/ha) = target plants/m2 x TGW (g) / expected establishment (%). The TGW is taken to the centigram and the establishment is in basis points, so kg/ha is exactly plants x centigrams / basis points and is rounded once. Doing it in floats gets 100 x 1.15 / 100 = 1.1499999999999999, which rounds to 1.1 kg/ha instead of 1.2. """ plants = target_plants_per_square_metre if isinstance(plants, bool) or not isinstance(plants, int) or plants < 1 or plants > 100000: raise ValueError("targetPlantsPerSquareMetre must be a whole number from 1 to 100000, received %r" % (plants,)) tgw = thousand_grain_weight_grams if isinstance(tgw, bool) or not isinstance(tgw, (int, float)) or not math.isfinite(tgw) or tgw <= 0 or tgw > 1000: raise ValueError("thousandGrainWeightGrams must be a finite number greater than 0 and at most 1000, received %r" % (tgw,)) centigrams = int(round(round_float(float(tgw), 2) * 100)) if centigrams < 1: raise ValueError("thousandGrainWeightGrams must be at least 0.01 grams, received %r" % (tgw,)) bp = establishment_basis_points if isinstance(bp, bool) or not isinstance(bp, int) or bp < 1 or bp > 10000: raise ValueError("establishmentBasisPoints must be a whole number from 1 to 10000, received %r" % (bp,)) return SeedRate( kg_per_hectare=round_div(plants * centigrams * 10, bp, "half-up") / 10, seeds_per_square_metre=round_div(plants * 100000, bp, "half-up") / 10, )