Rehost company bond slots and current stat seam
This commit is contained in:
parent
5372a11118
commit
2239f9ce72
5 changed files with 251 additions and 47 deletions
|
|
@ -5181,6 +5181,7 @@ mod tests {
|
|||
market_state: Some(crate::RuntimeCompanyMarketState {
|
||||
outstanding_shares: 20_000,
|
||||
bond_count: 2,
|
||||
live_bond_slots: Vec::new(),
|
||||
largest_live_bond_principal: Some(500_000),
|
||||
highest_coupon_live_bond_principal: Some(350_000),
|
||||
mutable_support_scalar_raw_u32: 0x3f99999a,
|
||||
|
|
@ -5235,6 +5236,7 @@ mod tests {
|
|||
market_state: Some(crate::RuntimeCompanyMarketState {
|
||||
outstanding_shares: 18_000,
|
||||
bond_count: 1,
|
||||
live_bond_slots: Vec::new(),
|
||||
largest_live_bond_principal: Some(300_000),
|
||||
highest_coupon_live_bond_principal: Some(300_000),
|
||||
mutable_support_scalar_raw_u32: 0x3f4ccccd,
|
||||
|
|
@ -6619,6 +6621,7 @@ mod tests {
|
|||
crate::RuntimeCompanyMarketState {
|
||||
outstanding_shares: 30_000,
|
||||
bond_count: 3,
|
||||
live_bond_slots: Vec::new(),
|
||||
largest_live_bond_principal: Some(750_000),
|
||||
highest_coupon_live_bond_principal: Some(500_000),
|
||||
mutable_support_scalar_raw_u32: 0x3f19999a,
|
||||
|
|
|
|||
|
|
@ -52,21 +52,22 @@ pub use runtime::{
|
|||
RuntimeCargoCatalogEntry, RuntimeCargoClass, RuntimeCargoPriceTarget,
|
||||
RuntimeCargoProductionTarget, RuntimeChairmanMetric, RuntimeChairmanProfile,
|
||||
RuntimeChairmanTarget, RuntimeCompany, RuntimeCompanyAnnualFinanceState,
|
||||
RuntimeCompanyConditionTestScope, RuntimeCompanyControllerKind, RuntimeCompanyMarketMetric,
|
||||
RuntimeCompanyMarketState, RuntimeCompanyMetric, RuntimeCompanyStatBandCandidate,
|
||||
RuntimeCompanyStatSelector, RuntimeCompanyTarget, RuntimeCompanyTerritoryAccess,
|
||||
RuntimeCompanyTerritoryTrackPieceCount, RuntimeCondition, RuntimeConditionComparator,
|
||||
RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate, RuntimeLocomotiveCatalogEntry,
|
||||
RuntimePackedEventCollectionSummary, RuntimePackedEventCompactControlSummary,
|
||||
RuntimePackedEventConditionRowSummary, RuntimePackedEventGroupedEffectRowSummary,
|
||||
RuntimePackedEventNegativeSentinelScopeSummary, RuntimePackedEventRecordSummary,
|
||||
RuntimePackedEventTextBandSummary, RuntimePlayer, RuntimePlayerConditionTestScope,
|
||||
RuntimePlayerTarget, RuntimeSaveProfileState, RuntimeServiceState, RuntimeState,
|
||||
RuntimeTerritory, RuntimeTerritoryMetric, RuntimeTerritoryTarget, RuntimeTrackMetric,
|
||||
RuntimeTrackPieceCounts, RuntimeTrain, RuntimeWorldFinanceNeighborhoodCandidate,
|
||||
RuntimeWorldIssueState, RuntimeWorldRestoreState, runtime_company_annual_finance_state,
|
||||
runtime_company_assigned_share_pool, runtime_company_market_value, runtime_company_prime_rate,
|
||||
runtime_company_stat_value, runtime_company_stat_value_f64,
|
||||
RuntimeCompanyBondSlot, RuntimeCompanyConditionTestScope, RuntimeCompanyControllerKind,
|
||||
RuntimeCompanyMarketMetric, RuntimeCompanyMarketState, RuntimeCompanyMetric,
|
||||
RuntimeCompanyStatBandCandidate, RuntimeCompanyStatSelector, RuntimeCompanyTarget,
|
||||
RuntimeCompanyTerritoryAccess, RuntimeCompanyTerritoryTrackPieceCount, RuntimeCondition,
|
||||
RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate,
|
||||
RuntimeLocomotiveCatalogEntry, RuntimePackedEventCollectionSummary,
|
||||
RuntimePackedEventCompactControlSummary, RuntimePackedEventConditionRowSummary,
|
||||
RuntimePackedEventGroupedEffectRowSummary, RuntimePackedEventNegativeSentinelScopeSummary,
|
||||
RuntimePackedEventRecordSummary, RuntimePackedEventTextBandSummary, RuntimePlayer,
|
||||
RuntimePlayerConditionTestScope, RuntimePlayerTarget, RuntimeSaveProfileState,
|
||||
RuntimeServiceState, RuntimeState, RuntimeTerritory, RuntimeTerritoryMetric,
|
||||
RuntimeTerritoryTarget, RuntimeTrackMetric, RuntimeTrackPieceCounts, RuntimeTrain,
|
||||
RuntimeWorldFinanceNeighborhoodCandidate, RuntimeWorldIssueState, RuntimeWorldRestoreState,
|
||||
runtime_company_annual_finance_state, runtime_company_assigned_share_pool,
|
||||
runtime_company_average_live_bond_coupon, runtime_company_market_value,
|
||||
runtime_company_prime_rate, runtime_company_stat_value, runtime_company_stat_value_f64,
|
||||
runtime_company_unassigned_share_pool, runtime_world_issue_opinion_multiplier,
|
||||
runtime_world_issue_opinion_term_sum_raw, runtime_world_issue_state,
|
||||
runtime_world_prime_rate_baseline,
|
||||
|
|
|
|||
|
|
@ -106,6 +106,15 @@ pub struct RuntimeCompanyMarketState {
|
|||
pub special_stat_family_232a_qword_bits: Vec<u64>,
|
||||
#[serde(default)]
|
||||
pub issue_opinion_terms_raw_i32: Vec<i32>,
|
||||
#[serde(default)]
|
||||
pub live_bond_slots: Vec<RuntimeCompanyBondSlot>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct RuntimeCompanyBondSlot {
|
||||
pub slot_index: u32,
|
||||
pub principal: u32,
|
||||
pub coupon_rate_raw_u32: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
|
@ -1982,6 +1991,16 @@ pub fn runtime_company_stat_value_f64(
|
|||
}
|
||||
}
|
||||
|
||||
fn runtime_company_current_stat_value_f64(
|
||||
state: &RuntimeState,
|
||||
company_id: u32,
|
||||
slot_id: u32,
|
||||
) -> Option<f64> {
|
||||
let market_state = state.service_state.company_market_state.get(&company_id)?;
|
||||
let index = slot_id.checked_mul(RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)? as usize;
|
||||
runtime_decode_saved_f64_bits(*market_state.year_stat_family_qword_bits.get(index)?)
|
||||
}
|
||||
|
||||
fn runtime_company_control_transfer_stat_value_f64(
|
||||
state: &RuntimeState,
|
||||
company_id: u32,
|
||||
|
|
@ -1992,7 +2011,16 @@ fn runtime_company_control_transfer_stat_value_f64(
|
|||
.iter()
|
||||
.find(|company| company.company_id == company_id)?;
|
||||
match slot_id {
|
||||
RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH => Some(company.current_cash as f64),
|
||||
0x00..=0x12 if slot_id != RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH => {
|
||||
runtime_company_current_stat_value_f64(state, company_id, slot_id)
|
||||
}
|
||||
RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH => {
|
||||
if company.current_cash != 0 {
|
||||
Some(company.current_cash as f64)
|
||||
} else {
|
||||
runtime_company_current_stat_value_f64(state, company_id, slot_id)
|
||||
}
|
||||
}
|
||||
RUNTIME_COMPANY_STAT_SLOT_BOOK_VALUE_PER_SHARE => Some(company.book_value_per_share as f64),
|
||||
_ => None,
|
||||
}
|
||||
|
|
@ -2273,6 +2301,30 @@ pub fn runtime_company_prime_rate(state: &RuntimeState, company_id: u32) -> Opti
|
|||
runtime_round_f64_to_i64(baseline + (raw_issue_sum as f64) * 0.01)
|
||||
}
|
||||
|
||||
pub fn runtime_company_average_live_bond_coupon(
|
||||
state: &RuntimeState,
|
||||
company_id: u32,
|
||||
) -> Option<f64> {
|
||||
let market_state = state.service_state.company_market_state.get(&company_id)?;
|
||||
if market_state.live_bond_slots.is_empty() {
|
||||
return Some(0.0);
|
||||
}
|
||||
let mut weighted_coupon_sum = 0.0f64;
|
||||
let mut total_principal = 0u64;
|
||||
for slot in &market_state.live_bond_slots {
|
||||
let coupon_rate = f32::from_bits(slot.coupon_rate_raw_u32) as f64;
|
||||
if !coupon_rate.is_finite() {
|
||||
continue;
|
||||
}
|
||||
weighted_coupon_sum += coupon_rate * (slot.principal as f64);
|
||||
total_principal = total_principal.checked_add(slot.principal as u64)?;
|
||||
}
|
||||
if total_principal == 0 {
|
||||
return Some(0.0);
|
||||
}
|
||||
Some(weighted_coupon_sum / total_principal as f64)
|
||||
}
|
||||
|
||||
pub fn runtime_world_absolute_counter(state: &RuntimeState) -> Option<u32> {
|
||||
state.world_restore.absolute_counter_raw_u32
|
||||
}
|
||||
|
|
@ -4403,6 +4455,13 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn reads_grounded_company_stat_family_slots_from_runtime_state() {
|
||||
let mut year_stat_family_qword_bits = vec![
|
||||
0u64;
|
||||
(RUNTIME_COMPANY_STAT_SLOT_COUNT * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
|
||||
as usize
|
||||
];
|
||||
year_stat_family_qword_bits[(0x12 * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN) as usize] =
|
||||
75.0f64.to_bits();
|
||||
let state = RuntimeState {
|
||||
calendar: CalendarPoint {
|
||||
year: 1830,
|
||||
|
|
@ -4460,7 +4519,16 @@ mod tests {
|
|||
territory_runtime_variables: BTreeMap::new(),
|
||||
world_scalar_overrides: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState::default(),
|
||||
service_state: RuntimeServiceState {
|
||||
company_market_state: BTreeMap::from([(
|
||||
7,
|
||||
RuntimeCompanyMarketState {
|
||||
year_stat_family_qword_bits,
|
||||
..RuntimeCompanyMarketState::default()
|
||||
},
|
||||
)]),
|
||||
..RuntimeServiceState::default()
|
||||
},
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
|
|
@ -4485,6 +4553,17 @@ mod tests {
|
|||
),
|
||||
Some(2_620)
|
||||
);
|
||||
assert_eq!(
|
||||
runtime_company_stat_value(
|
||||
&state,
|
||||
7,
|
||||
RuntimeCompanyStatSelector {
|
||||
family_id: RUNTIME_COMPANY_STAT_FAMILY_CONTROL_TRANSFER,
|
||||
slot_id: 0x12,
|
||||
},
|
||||
),
|
||||
Some(75)
|
||||
);
|
||||
assert_eq!(
|
||||
runtime_company_stat_value(
|
||||
&state,
|
||||
|
|
@ -4494,7 +4573,7 @@ mod tests {
|
|||
slot_id: 0x2b,
|
||||
},
|
||||
),
|
||||
None
|
||||
Some(0)
|
||||
);
|
||||
assert_eq!(
|
||||
runtime_company_stat_value(
|
||||
|
|
@ -5327,6 +5406,93 @@ mod tests {
|
|||
assert_eq!(runtime_company_prime_rate(&state, 7), Some(7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn computes_weighted_average_live_bond_coupon_from_owned_market_slots() {
|
||||
let state = RuntimeState {
|
||||
calendar: CalendarPoint {
|
||||
year: 1830,
|
||||
month_slot: 0,
|
||||
phase_slot: 0,
|
||||
tick_slot: 0,
|
||||
},
|
||||
world_flags: BTreeMap::new(),
|
||||
save_profile: RuntimeSaveProfileState::default(),
|
||||
world_restore: RuntimeWorldRestoreState::default(),
|
||||
metadata: BTreeMap::new(),
|
||||
companies: vec![RuntimeCompany {
|
||||
company_id: 7,
|
||||
current_cash: 0,
|
||||
debt: 0,
|
||||
credit_rating_score: None,
|
||||
prime_rate: None,
|
||||
active: true,
|
||||
available_track_laying_capacity: None,
|
||||
controller_kind: RuntimeCompanyControllerKind::Unknown,
|
||||
linked_chairman_profile_id: None,
|
||||
book_value_per_share: 0,
|
||||
investor_confidence: 0,
|
||||
management_attitude: 0,
|
||||
takeover_cooldown_year: None,
|
||||
merger_cooldown_year: None,
|
||||
track_piece_counts: RuntimeTrackPieceCounts::default(),
|
||||
}],
|
||||
selected_company_id: None,
|
||||
players: Vec::new(),
|
||||
selected_player_id: None,
|
||||
chairman_profiles: Vec::new(),
|
||||
selected_chairman_profile_id: None,
|
||||
trains: Vec::new(),
|
||||
locomotive_catalog: Vec::new(),
|
||||
cargo_catalog: Vec::new(),
|
||||
territories: Vec::new(),
|
||||
company_territory_track_piece_counts: Vec::new(),
|
||||
company_territory_access: Vec::new(),
|
||||
packed_event_collection: None,
|
||||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
named_locomotive_availability: BTreeMap::new(),
|
||||
named_locomotive_cost: BTreeMap::new(),
|
||||
all_cargo_price_override: None,
|
||||
named_cargo_price_overrides: BTreeMap::new(),
|
||||
all_cargo_production_override: None,
|
||||
factory_cargo_production_override: None,
|
||||
farm_mine_cargo_production_override: None,
|
||||
named_cargo_production_overrides: BTreeMap::new(),
|
||||
cargo_production_overrides: BTreeMap::new(),
|
||||
world_runtime_variables: BTreeMap::new(),
|
||||
company_runtime_variables: BTreeMap::new(),
|
||||
player_runtime_variables: BTreeMap::new(),
|
||||
territory_runtime_variables: BTreeMap::new(),
|
||||
world_scalar_overrides: BTreeMap::new(),
|
||||
special_conditions: BTreeMap::new(),
|
||||
service_state: RuntimeServiceState {
|
||||
company_market_state: BTreeMap::from([(
|
||||
7,
|
||||
RuntimeCompanyMarketState {
|
||||
live_bond_slots: vec![
|
||||
RuntimeCompanyBondSlot {
|
||||
slot_index: 0,
|
||||
principal: 100_000,
|
||||
coupon_rate_raw_u32: 0.04f32.to_bits(),
|
||||
},
|
||||
RuntimeCompanyBondSlot {
|
||||
slot_index: 1,
|
||||
principal: 300_000,
|
||||
coupon_rate_raw_u32: 0.08f32.to_bits(),
|
||||
},
|
||||
],
|
||||
..RuntimeCompanyMarketState::default()
|
||||
},
|
||||
)]),
|
||||
..RuntimeServiceState::default()
|
||||
},
|
||||
};
|
||||
|
||||
let average = runtime_company_average_live_bond_coupon(&state, 7)
|
||||
.expect("weighted average live bond coupon");
|
||||
assert!((average - 0.07).abs() < 1e-6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decodes_and_packs_company_issue_calendar_tuple() {
|
||||
let tuple = runtime_decode_packed_calendar_tuple(0x0101_0726, 0x0001_0001);
|
||||
|
|
|
|||
|
|
@ -2388,6 +2388,8 @@ pub struct SmpSaveCompanyRecordAnalysisEntry {
|
|||
pub debt: u64,
|
||||
pub bond_count: u8,
|
||||
#[serde(default)]
|
||||
pub live_bond_slots: Vec<crate::RuntimeCompanyBondSlot>,
|
||||
#[serde(default)]
|
||||
pub largest_live_bond_principal: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub highest_coupon_live_bond_principal: Option<u32>,
|
||||
|
|
@ -3065,6 +3067,7 @@ pub fn inspect_save_company_and_chairman_analysis_bytes(
|
|||
&bytes,
|
||||
record_offset + SAVE_COMPANY_RECORD_BOND_COUNT_OFFSET,
|
||||
)?;
|
||||
let live_bond_slots = parse_save_company_live_bond_slots(&bytes, record_offset)?;
|
||||
let largest_live_bond_principal =
|
||||
parse_save_company_largest_live_bond_principal(&bytes, record_offset)?;
|
||||
let highest_coupon_live_bond_principal =
|
||||
|
|
@ -3168,6 +3171,7 @@ pub fn inspect_save_company_and_chairman_analysis_bytes(
|
|||
outstanding_shares,
|
||||
debt,
|
||||
bond_count,
|
||||
live_bond_slots,
|
||||
largest_live_bond_principal,
|
||||
highest_coupon_live_bond_principal,
|
||||
available_track_laying_capacity,
|
||||
|
|
@ -3697,6 +3701,7 @@ fn parse_save_company_roster_probe(
|
|||
)?;
|
||||
let debt = parse_save_company_total_debt(bytes, record_offset)?;
|
||||
let bond_count = read_u8_at(bytes, record_offset + SAVE_COMPANY_RECORD_BOND_COUNT_OFFSET)?;
|
||||
let live_bond_slots = parse_save_company_live_bond_slots(bytes, record_offset)?;
|
||||
let largest_live_bond_principal =
|
||||
parse_save_company_largest_live_bond_principal(bytes, record_offset)?;
|
||||
let highest_coupon_live_bond_principal =
|
||||
|
|
@ -3822,11 +3827,17 @@ fn parse_save_company_roster_probe(
|
|||
SAVE_COMPANY_RECORD_ISSUE_OPINION_TERMS_OFFSET,
|
||||
SAVE_COMPANY_RECORD_ISSUE_OPINION_TERM_COUNT,
|
||||
)?;
|
||||
let current_cash = decode_save_company_current_year_stat_slot(
|
||||
&year_stat_family_qword_bits,
|
||||
crate::RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH,
|
||||
)
|
||||
.and_then(round_f64_to_i64)
|
||||
.unwrap_or(0);
|
||||
entries.push(SmpLoadedCompanyRosterEntry {
|
||||
company_id,
|
||||
active,
|
||||
controller_kind: RuntimeCompanyControllerKind::Unknown,
|
||||
current_cash: 0,
|
||||
current_cash,
|
||||
debt,
|
||||
credit_rating_score: None,
|
||||
prime_rate: None,
|
||||
|
|
@ -3841,6 +3852,7 @@ fn parse_save_company_roster_probe(
|
|||
market_state: Some(RuntimeCompanyMarketState {
|
||||
outstanding_shares,
|
||||
bond_count,
|
||||
live_bond_slots,
|
||||
largest_live_bond_principal,
|
||||
highest_coupon_live_bond_principal,
|
||||
mutable_support_scalar_raw_u32,
|
||||
|
|
@ -3947,6 +3959,15 @@ fn build_save_i32_term_strip(
|
|||
.collect::<Option<Vec<_>>>()
|
||||
}
|
||||
|
||||
fn decode_save_company_current_year_stat_slot(
|
||||
year_stat_family_qword_bits: &[u64],
|
||||
slot_id: u32,
|
||||
) -> Option<f64> {
|
||||
let index = slot_id.checked_mul(crate::RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)? as usize;
|
||||
let value = f64::from_bits(*year_stat_family_qword_bits.get(index)?);
|
||||
value.is_finite().then_some(value)
|
||||
}
|
||||
|
||||
fn detect_save_company_record_start_offset(
|
||||
bytes: &[u8],
|
||||
header_probe: &SmpSaveTaggedCollectionHeaderProbe,
|
||||
|
|
@ -4054,37 +4075,13 @@ fn parse_save_company_total_debt(bytes: &[u8], record_offset: usize) -> Option<u
|
|||
Some(total)
|
||||
}
|
||||
|
||||
fn parse_save_company_largest_live_bond_principal(
|
||||
fn parse_save_company_live_bond_slots(
|
||||
bytes: &[u8],
|
||||
record_offset: usize,
|
||||
) -> Option<Option<u32>> {
|
||||
) -> Option<Vec<crate::RuntimeCompanyBondSlot>> {
|
||||
let bond_count =
|
||||
read_u8_at(bytes, record_offset + SAVE_COMPANY_RECORD_BOND_COUNT_OFFSET)? as usize;
|
||||
let mut largest_live_principal: Option<u32> = None;
|
||||
for slot_index in 0..bond_count {
|
||||
let slot_offset = record_offset
|
||||
.checked_add(SAVE_COMPANY_RECORD_BOND_TABLE_OFFSET)?
|
||||
.checked_add(slot_index.checked_mul(SAVE_COMPANY_RECORD_BOND_SLOT_STRIDE)?)?;
|
||||
let principal = read_i32_at(bytes, slot_offset)?;
|
||||
if principal > 0 {
|
||||
let principal = principal as u32;
|
||||
largest_live_principal = Some(match largest_live_principal {
|
||||
Some(current) => current.max(principal),
|
||||
None => principal,
|
||||
});
|
||||
}
|
||||
}
|
||||
Some(largest_live_principal)
|
||||
}
|
||||
|
||||
fn parse_save_company_highest_coupon_live_bond_principal(
|
||||
bytes: &[u8],
|
||||
record_offset: usize,
|
||||
) -> Option<Option<u32>> {
|
||||
let bond_count =
|
||||
read_u8_at(bytes, record_offset + SAVE_COMPANY_RECORD_BOND_COUNT_OFFSET)? as usize;
|
||||
let mut highest_coupon_principal = None;
|
||||
let mut highest_coupon_rate = None;
|
||||
let mut slots = Vec::new();
|
||||
for slot_index in 0..bond_count {
|
||||
let slot_offset = record_offset
|
||||
.checked_add(SAVE_COMPANY_RECORD_BOND_TABLE_OFFSET)?
|
||||
|
|
@ -4098,19 +4095,49 @@ fn parse_save_company_highest_coupon_live_bond_principal(
|
|||
if !coupon_rate.is_finite() {
|
||||
continue;
|
||||
}
|
||||
let principal = principal as u32;
|
||||
slots.push(crate::RuntimeCompanyBondSlot {
|
||||
slot_index: slot_index as u32,
|
||||
principal: principal as u32,
|
||||
coupon_rate_raw_u32,
|
||||
});
|
||||
}
|
||||
Some(slots)
|
||||
}
|
||||
|
||||
fn parse_save_company_largest_live_bond_principal(
|
||||
bytes: &[u8],
|
||||
record_offset: usize,
|
||||
) -> Option<Option<u32>> {
|
||||
let mut largest_live_principal: Option<u32> = None;
|
||||
for slot in parse_save_company_live_bond_slots(bytes, record_offset)? {
|
||||
largest_live_principal = Some(match largest_live_principal {
|
||||
Some(current) => current.max(slot.principal),
|
||||
None => slot.principal,
|
||||
});
|
||||
}
|
||||
Some(largest_live_principal)
|
||||
}
|
||||
|
||||
fn parse_save_company_highest_coupon_live_bond_principal(
|
||||
bytes: &[u8],
|
||||
record_offset: usize,
|
||||
) -> Option<Option<u32>> {
|
||||
let mut highest_coupon_principal = None;
|
||||
let mut highest_coupon_rate = None;
|
||||
for slot in parse_save_company_live_bond_slots(bytes, record_offset)? {
|
||||
let coupon_rate = f32::from_bits(slot.coupon_rate_raw_u32);
|
||||
match highest_coupon_rate {
|
||||
Some(current_rate) if coupon_rate < current_rate => {}
|
||||
Some(current_rate) if coupon_rate == current_rate => {
|
||||
if let Some(current_principal) = highest_coupon_principal {
|
||||
if principal > current_principal {
|
||||
highest_coupon_principal = Some(principal);
|
||||
if slot.principal > current_principal {
|
||||
highest_coupon_principal = Some(slot.principal);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
highest_coupon_rate = Some(coupon_rate);
|
||||
highest_coupon_principal = Some(principal);
|
||||
highest_coupon_principal = Some(slot.principal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16466,6 +16493,12 @@ mod tests {
|
|||
.as_ref()
|
||||
.expect("company market state should load");
|
||||
assert_eq!(market_state.outstanding_shares, 20_000);
|
||||
assert_eq!(market_state.live_bond_slots.len(), 2);
|
||||
assert_eq!(market_state.live_bond_slots[0].principal, 900_000);
|
||||
assert_eq!(
|
||||
market_state.live_bond_slots[1].coupon_rate_raw_u32,
|
||||
0.12f32.to_bits()
|
||||
);
|
||||
assert_eq!(market_state.largest_live_bond_principal, Some(900_000));
|
||||
assert_eq!(
|
||||
market_state.highest_coupon_live_bond_principal,
|
||||
|
|
|
|||
|
|
@ -2039,6 +2039,7 @@ mod tests {
|
|||
crate::RuntimeCompanyMarketState {
|
||||
outstanding_shares: 20_000,
|
||||
bond_count: 2,
|
||||
live_bond_slots: Vec::new(),
|
||||
highest_coupon_live_bond_principal: Some(350_000),
|
||||
largest_live_bond_principal: Some(500_000),
|
||||
mutable_support_scalar_raw_u32: 0x3f800000,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue