use std::io::{self, Read, Write};
use bcinr_cmca::allocator::allocate;
use bcinr_cmca::fixed::NonNegativeFixed;
use bcinr_cmca::generated::consequence_mass::case_studies::{
PackedSemanticState, ETA, FACTOR_ACCESS_FREQUENCY, FACTOR_BUSINESS_VALUE,
FACTOR_DOWNSTREAM_CONSEQUENCE, FACTOR_RECOMPUTATION_COST, FACTOR_RETRIEVAL_DEMAND,
FACTOR_SCHEDULING_DEMAND, FACTOR_SEARCH_DEMAND, FACTOR_STANDING, FACTOR_VERIFICATION_COST,
LAMBDA, LENS_REGISTRY, N, Q,
};
use bcinr_cmca::generated::stability_profile::CERTIFICATE_DIGEST;
use serde::{Deserialize, Serialize};
use serde_json::Value;
const MAX_CANDIDATES: usize = N;
const NUM_MEASURES: usize = 4;
#[derive(Debug, Deserialize)]
struct Candidate {
name: String,
measures: [f64; NUM_MEASURES],
}
#[derive(Debug, Deserialize)]
struct RankRequest {
candidates: Vec<Candidate>,
}
#[derive(Debug, Serialize)]
struct RankedCandidate {
name: String,
share: f64,
}
#[derive(Debug, Serialize)]
struct RankResponse {
ranking: Vec<RankedCandidate>,
}
fn error_envelope(message: String) -> Value {
serde_json::json!({ "error": message })
}
fn to_fixed(v: f64) -> NonNegativeFixed {
let clamped = v.max(0.0);
NonNegativeFixed::from_bits((clamped * 65536.0).round() as u32)
}
fn to_f64(f: NonNegativeFixed) -> f64 {
(f.val as f64) / 65536.0
}
fn phantom_state(id: usize) -> PackedSemanticState {
PackedSemanticState {
id: id as u32,
factors: [NonNegativeFixed::ZERO; 10],
}
}
fn state_from_measures(id: usize, measures: &[f64; NUM_MEASURES]) -> PackedSemanticState {
let mut factors = [NonNegativeFixed::ZERO; 10];
factors[FACTOR_ACCESS_FREQUENCY] = NonNegativeFixed::ONE;
factors[FACTOR_STANDING] = NonNegativeFixed::ONE;
factors[FACTOR_VERIFICATION_COST] = NonNegativeFixed::ZERO;
factors[FACTOR_RECOMPUTATION_COST] = to_fixed(measures[0]) / NonNegativeFixed::from_num(5);
factors[FACTOR_BUSINESS_VALUE] = NonNegativeFixed::ONE;
factors[FACTOR_DOWNSTREAM_CONSEQUENCE] = NonNegativeFixed::ZERO;
factors[FACTOR_SEARCH_DEMAND] = to_fixed(measures[1]);
factors[FACTOR_RETRIEVAL_DEMAND] = to_fixed(measures[2]);
factors[FACTOR_SCHEDULING_DEMAND] = to_fixed(measures[3]);
PackedSemanticState {
id: id as u32,
factors,
}
}
fn run() -> Result<Value, String> {
let mut input = String::new();
io::stdin()
.read_to_string(&mut input)
.map_err(|e| format!("failed to read stdin: {e}"))?;
let request: RankRequest =
serde_json::from_str(&input).map_err(|e| format!("invalid request JSON: {e}"))?;
if request.candidates.len() > MAX_CANDIDATES {
return Err(format!(
"too many candidates: got {}, allocate() is compiled for at most N={} (CMCA-108)",
request.candidates.len(),
MAX_CANDIDATES
));
}
if request.candidates.is_empty() {
return Err("no candidates provided".to_string());
}
let mut states: [PackedSemanticState; N] = core::array::from_fn(phantom_state);
let mut real_names: Vec<String> = Vec::with_capacity(request.candidates.len());
for (i, candidate) in request.candidates.iter().enumerate() {
states[i] = state_from_measures(i, &candidate.measures);
real_names.push(candidate.name.clone());
}
let mut weights = [[NonNegativeFixed::ONE; 2 * Q]; N];
let payoffs = [[NonNegativeFixed::ZERO; 2 * Q]; N];
let mut last_switch_t = 0;
let mut prev_mode = 0;
let parent = [-1; N];
let mu = [NonNegativeFixed::ZERO; N];
let costs = [NonNegativeFixed::ZERO; N];
let result = allocate(
&states,
&LENS_REGISTRY,
&LAMBDA,
ETA,
&parent,
&mut weights,
&payoffs,
NonNegativeFixed::ZERO,
NonNegativeFixed::ZERO,
&mu,
&costs,
0,
&mut last_switch_t,
&mut prev_mode,
500,
CERTIFICATE_DIGEST,
None,
)
.map_err(|e| format!("allocate() refused: {e:?}"))?;
let mut ranking: Vec<RankedCandidate> = real_names
.iter()
.enumerate()
.map(|(i, name)| RankedCandidate {
name: name.clone(),
share: to_f64(result[i]),
})
.collect();
ranking.sort_by(|a, b| b.share.total_cmp(&a.share));
let response = RankResponse { ranking };
serde_json::to_value(response).map_err(|e| format!("failed to serialize response: {e}"))
}
fn main() {
match run() {
Ok(value) => {
println!("{value}");
}
Err(message) => {
let stderr = io::stderr();
let mut handle = stderr.lock();
let _ = writeln!(handle, "{}", error_envelope(message));
std::process::exit(1);
}
}
}