1use crate::ir_nodes::{IRFlowNode, IRProgram};
13use serde::Serialize;
14
15#[derive(Debug, Clone, Serialize)]
19pub struct PricingModel {
20 pub name: String,
21 pub input_per_million: f64,
22 pub output_per_million: f64,
23}
24
25impl PricingModel {
26 pub fn default_sonnet() -> Self {
28 PricingModel {
29 name: "claude-sonnet-4".to_string(),
30 input_per_million: 3.0,
31 output_per_million: 15.0,
32 }
33 }
34
35 pub fn opus() -> Self {
37 PricingModel {
38 name: "claude-opus-4".to_string(),
39 input_per_million: 15.0,
40 output_per_million: 75.0,
41 }
42 }
43
44 pub fn haiku() -> Self {
46 PricingModel {
47 name: "claude-haiku-3.5".to_string(),
48 input_per_million: 0.80,
49 output_per_million: 4.0,
50 }
51 }
52
53 pub fn compute_cost(&self, input_tokens: u64, output_tokens: u64) -> f64 {
55 let input_cost = (input_tokens as f64 / 1_000_000.0) * self.input_per_million;
56 let output_cost = (output_tokens as f64 / 1_000_000.0) * self.output_per_million;
57 input_cost + output_cost
58 }
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
65#[serde(rename_all = "snake_case")]
66pub enum StepKind {
67 Ask,
69 ToolCall,
71 Reason,
73 Probe,
75 Validate,
77 Refine,
79 Weave,
81 Memory,
83 Control,
85 Parallel,
87 MultiAgent,
89 Cognitive,
91}
92
93#[derive(Debug, Clone, Serialize)]
95pub struct StepEstimate {
96 pub kind: StepKind,
97 pub input_tokens: u64,
98 pub output_tokens: u64,
99}
100
101fn default_estimate(kind: StepKind) -> StepEstimate {
103 let (input, output) = match kind {
104 StepKind::Ask => (800, 400),
105 StepKind::ToolCall => (1000, 300),
106 StepKind::Reason => (1200, 800),
107 StepKind::Probe => (600, 200),
108 StepKind::Validate => (700, 300),
109 StepKind::Refine => (900, 600),
110 StepKind::Weave => (1500, 600),
111 StepKind::Memory => (100, 50),
112 StepKind::Control => (0, 0),
113 StepKind::Parallel => (0, 0),
114 StepKind::MultiAgent => (2000, 1000),
115 StepKind::Cognitive => (800, 400),
116 };
117 StepEstimate { kind, input_tokens: input, output_tokens: output }
118}
119
120fn classify_node(node: &IRFlowNode) -> StepKind {
124 match node {
125 IRFlowNode::Step(_) => StepKind::Ask,
126 IRFlowNode::UseTool(_) => StepKind::ToolCall,
127 IRFlowNode::Reason(_) => StepKind::Reason,
128 IRFlowNode::Probe(_) => StepKind::Probe,
129 IRFlowNode::Validate(_) => StepKind::Validate,
130 IRFlowNode::Refine(_) => StepKind::Refine,
131 IRFlowNode::Weave(_) => StepKind::Weave,
132 IRFlowNode::Remember(_) | IRFlowNode::Recall(_)
133 | IRFlowNode::Persist(_) | IRFlowNode::Retrieve(_)
134 | IRFlowNode::Mutate(_) | IRFlowNode::Purge(_) => StepKind::Memory,
135 IRFlowNode::Conditional(_) | IRFlowNode::ForIn(_)
136 | IRFlowNode::Let(_) | IRFlowNode::Return(_)
137 | IRFlowNode::Break(_) | IRFlowNode::Continue(_) => StepKind::Control,
140 IRFlowNode::Par(_) | IRFlowNode::Stream(_) => StepKind::Parallel,
141 IRFlowNode::Deliberate(_) | IRFlowNode::Consensus(_)
142 | IRFlowNode::Forge(_) => StepKind::MultiAgent,
143 IRFlowNode::Focus(_) | IRFlowNode::Associate(_)
144 | IRFlowNode::Aggregate(_) | IRFlowNode::Explore(_)
145 | IRFlowNode::Ingest(_) | IRFlowNode::Navigate(_)
146 | IRFlowNode::Drill(_) | IRFlowNode::Trail(_)
147 | IRFlowNode::Corroborate(_) | IRFlowNode::Listen(_)
148 | IRFlowNode::DaemonStep(_) | IRFlowNode::Hibernate(_) => StepKind::Cognitive,
149 IRFlowNode::ShieldApply(_) | IRFlowNode::OtsApply(_)
150 | IRFlowNode::MandateApply(_) | IRFlowNode::ComputeApply(_)
151 | IRFlowNode::LambdaDataApply(_) | IRFlowNode::Transact(_) => StepKind::Control,
152 IRFlowNode::Emit(_) | IRFlowNode::Publish(_) | IRFlowNode::Discover(_) => StepKind::Cognitive,
155 }
156}
157
158fn count_steps(nodes: &[IRFlowNode]) -> Vec<(StepKind, u32)> {
160 let mut counts = std::collections::HashMap::new();
161
162 fn walk(nodes: &[IRFlowNode], counts: &mut std::collections::HashMap<StepKind, u32>) {
163 for node in nodes {
164 let kind = classify_node(node);
165 *counts.entry(kind).or_insert(0) += 1;
166
167 match node {
169 IRFlowNode::Conditional(c) => {
170 walk(&c.then_body, counts);
171 walk(&c.else_body, counts);
172 }
173 IRFlowNode::ForIn(f) => walk(&f.body, counts),
174 _ => {}
177 }
178 }
179 }
180
181 walk(nodes, &mut counts);
182 let mut result: Vec<_> = counts.into_iter().collect();
183 result.sort_by_key(|(k, _)| format!("{:?}", k));
184 result
185}
186
187#[derive(Debug, Clone, Serialize)]
191pub struct FlowCostEstimate {
192 pub flow_name: String,
193 pub step_counts: Vec<StepCountEntry>,
194 pub total_steps: u32,
195 pub estimated_input_tokens: u64,
196 pub estimated_output_tokens: u64,
197 pub estimated_total_tokens: u64,
198}
199
200#[derive(Debug, Clone, Serialize)]
202pub struct StepCountEntry {
203 pub kind: StepKind,
204 pub count: u32,
205 pub input_tokens: u64,
206 pub output_tokens: u64,
207}
208
209#[derive(Debug, Clone, Serialize)]
211pub struct CostReport {
212 pub pricing: PricingModel,
213 pub flows: Vec<FlowCostEstimate>,
214 pub total_input_tokens: u64,
215 pub total_output_tokens: u64,
216 pub total_tokens: u64,
217 pub estimated_cost_usd: f64,
218}
219
220pub fn estimate_program(ir: &IRProgram, pricing: &PricingModel) -> CostReport {
222 let mut flows = Vec::new();
223 let mut total_input: u64 = 0;
224 let mut total_output: u64 = 0;
225
226 for flow in &ir.flows {
227 let step_counts = count_steps(&flow.steps);
228 let mut flow_input: u64 = 0;
229 let mut flow_output: u64 = 0;
230 let mut total_steps: u32 = 0;
231
232 let entries: Vec<StepCountEntry> = step_counts
233 .iter()
234 .map(|(kind, count)| {
235 let est = default_estimate(*kind);
236 let input = est.input_tokens * (*count as u64);
237 let output = est.output_tokens * (*count as u64);
238 flow_input += input;
239 flow_output += output;
240 total_steps += count;
241 StepCountEntry {
242 kind: *kind,
243 count: *count,
244 input_tokens: input,
245 output_tokens: output,
246 }
247 })
248 .collect();
249
250 total_input += flow_input;
251 total_output += flow_output;
252
253 flows.push(FlowCostEstimate {
254 flow_name: flow.name.clone(),
255 step_counts: entries,
256 total_steps,
257 estimated_input_tokens: flow_input,
258 estimated_output_tokens: flow_output,
259 estimated_total_tokens: flow_input + flow_output,
260 });
261 }
262
263 let cost = pricing.compute_cost(total_input, total_output);
264
265 CostReport {
266 pricing: pricing.clone(),
267 flows,
268 total_input_tokens: total_input,
269 total_output_tokens: total_output,
270 total_tokens: total_input + total_output,
271 estimated_cost_usd: cost,
272 }
273}
274
275pub fn format_text(report: &CostReport) -> String {
279 let mut out = String::new();
280
281 out.push_str(&format!("AXON Execution Cost Estimate ({})\n", report.pricing.name));
282 out.push_str(&format!("Pricing: ${}/M input, ${}/M output\n",
283 report.pricing.input_per_million, report.pricing.output_per_million));
284 out.push_str(&"─".repeat(60));
285 out.push('\n');
286
287 for flow in &report.flows {
288 out.push_str(&format!("\nFlow: {}\n", flow.flow_name));
289 out.push_str(&format!(" Steps: {}\n", flow.total_steps));
290
291 for entry in &flow.step_counts {
292 if entry.count > 0 {
293 out.push_str(&format!(" {:12} x{:<3} ~{} input + {} output tokens\n",
294 format!("{:?}", entry.kind),
295 entry.count,
296 entry.input_tokens,
297 entry.output_tokens,
298 ));
299 }
300 }
301
302 out.push_str(&format!(" Subtotal: ~{} tokens ({} in + {} out)\n",
303 flow.estimated_total_tokens,
304 flow.estimated_input_tokens,
305 flow.estimated_output_tokens,
306 ));
307 }
308
309 out.push_str(&format!("\n{}\n", "─".repeat(60)));
310 out.push_str(&format!("Total: ~{} tokens ({} in + {} out)\n",
311 report.total_tokens, report.total_input_tokens, report.total_output_tokens));
312 out.push_str(&format!("Estimated cost: ${:.6} USD\n", report.estimated_cost_usd));
313
314 out
315}
316
317pub fn run_estimate(file: &str, format: &str, model: &str) -> i32 {
319 let source = match std::fs::read_to_string(file) {
320 Ok(s) => s,
321 Err(e) => {
322 eprintln!("Error reading {}: {}", file, e);
323 return 1;
324 }
325 };
326
327 let tokens = match crate::lexer::Lexer::new(&source, file).tokenize() {
328 Ok(t) => t,
329 Err(e) => {
330 eprintln!("Lexer error: {:?}", e);
331 return 1;
332 }
333 };
334 let ast = match crate::parser::Parser::new(tokens).parse() {
335 Ok(ast) => ast,
336 Err(e) => {
337 eprintln!("Parse error: {:?}", e);
338 return 1;
339 }
340 };
341
342 let ir = crate::ir_generator::IRGenerator::new().generate(&ast);
343
344 let pricing = match model {
345 "opus" => PricingModel::opus(),
346 "haiku" => PricingModel::haiku(),
347 _ => PricingModel::default_sonnet(),
348 };
349
350 let report = estimate_program(&ir, &pricing);
351
352 match format {
353 "json" => {
354 println!("{}", serde_json::to_string_pretty(&report).unwrap_or_default());
355 }
356 _ => {
357 print!("{}", format_text(&report));
358 }
359 }
360
361 0
362}
363
364#[cfg(test)]
367mod tests {
368 use super::*;
369
370 #[test]
371 fn pricing_sonnet_defaults() {
372 let p = PricingModel::default_sonnet();
373 assert_eq!(p.input_per_million, 3.0);
374 assert_eq!(p.output_per_million, 15.0);
375 }
376
377 #[test]
378 fn pricing_compute_cost() {
379 let p = PricingModel::default_sonnet();
380 let cost = p.compute_cost(1_000_000, 1_000_000);
382 assert!((cost - 18.0).abs() < 0.001);
383 }
384
385 #[test]
386 fn pricing_zero_tokens() {
387 let p = PricingModel::default_sonnet();
388 assert_eq!(p.compute_cost(0, 0), 0.0);
389 }
390
391 #[test]
392 fn pricing_opus_rates() {
393 let p = PricingModel::opus();
394 assert_eq!(p.input_per_million, 15.0);
395 assert_eq!(p.output_per_million, 75.0);
396 let cost = p.compute_cost(1_000_000, 1_000_000);
397 assert!((cost - 90.0).abs() < 0.001);
398 }
399
400 #[test]
401 fn pricing_haiku_rates() {
402 let p = PricingModel::haiku();
403 assert_eq!(p.input_per_million, 0.80);
404 assert_eq!(p.output_per_million, 4.0);
405 }
406
407 #[test]
408 fn classify_step_kinds() {
409 use crate::ir_nodes::*;
410
411 let step = IRFlowNode::Step(IRStep {
412 node_type: "Step",
413 source_line: 1, source_column: 1,
414 name: "s1".into(), persona_ref: "".into(),
415 given: "".into(), ask: "do something".into(),
416 use_tool: None, probe: None, reason: None, weave: None,
417 output_type: "".into(), confidence_floor: None,
418 navigate_ref: "".into(), apply_ref: "".into(),
419 body: vec![],
420 });
421 assert_eq!(classify_node(&step), StepKind::Ask);
422
423 let tool = IRFlowNode::UseTool(IRUseToolStep {
424 node_type: "UseTool",
425 source_line: 1, source_column: 1,
426 tool_name: "search".into(), argument: "q".into(),
427 named_args: Vec::new(),
428 });
429 assert_eq!(classify_node(&tool), StepKind::ToolCall);
430
431 let reason = IRFlowNode::Reason(IRReasonStep {
432 node_type: "Reason",
433 source_line: 1, source_column: 1,
434 strategy: "deductive".into(), target: "t".into(),
435 });
436 assert_eq!(classify_node(&reason), StepKind::Reason);
437 }
438
439 #[test]
440 fn count_steps_flat() {
441 use crate::ir_nodes::*;
442
443 let nodes = vec![
444 IRFlowNode::Step(IRStep {
445 node_type: "Step", source_line: 1, source_column: 1,
446 name: "s1".into(), persona_ref: "".into(),
447 given: "".into(), ask: "a".into(),
448 use_tool: None, probe: None, reason: None, weave: None,
449 output_type: "".into(), confidence_floor: None,
450 navigate_ref: "".into(), apply_ref: "".into(),
451 body: vec![],
452 }),
453 IRFlowNode::Step(IRStep {
454 node_type: "Step", source_line: 2, source_column: 1,
455 name: "s2".into(), persona_ref: "".into(),
456 given: "".into(), ask: "b".into(),
457 use_tool: None, probe: None, reason: None, weave: None,
458 output_type: "".into(), confidence_floor: None,
459 navigate_ref: "".into(), apply_ref: "".into(),
460 body: vec![],
461 }),
462 IRFlowNode::UseTool(IRUseToolStep {
463 node_type: "UseTool", source_line: 3, source_column: 1,
464 tool_name: "t".into(), argument: "a".into(),
465 named_args: Vec::new(),
466 }),
467 ];
468
469 let counts = count_steps(&nodes);
470 let ask_count = counts.iter().find(|(k, _)| *k == StepKind::Ask).map(|(_, c)| *c).unwrap_or(0);
471 let tool_count = counts.iter().find(|(k, _)| *k == StepKind::ToolCall).map(|(_, c)| *c).unwrap_or(0);
472 assert_eq!(ask_count, 2);
473 assert_eq!(tool_count, 1);
474 }
475
476 #[test]
477 fn count_steps_nested_conditional() {
478 use crate::ir_nodes::*;
479
480 let inner_step = IRFlowNode::Step(IRStep {
481 node_type: "Step", source_line: 1, source_column: 1,
482 name: "inner".into(), persona_ref: "".into(),
483 given: "".into(), ask: "x".into(),
484 use_tool: None, probe: None, reason: None, weave: None,
485 output_type: "".into(), confidence_floor: None,
486 navigate_ref: "".into(), apply_ref: "".into(),
487 body: vec![],
488 });
489
490 let cond = IRFlowNode::Conditional(IRConditional {
491 node_type: "Conditional", source_line: 1, source_column: 1,
492 condition: "c".into(), comparison_op: "==".into(),
493 comparison_value: "true".into(),
494 then_body: vec![inner_step],
495 else_body: vec![],
496 conditions: vec![],
497 conjunctor: "".into(),
498 });
499
500 let counts = count_steps(&[cond]);
501 let control = counts.iter().find(|(k, _)| *k == StepKind::Control).map(|(_, c)| *c).unwrap_or(0);
502 let ask = counts.iter().find(|(k, _)| *k == StepKind::Ask).map(|(_, c)| *c).unwrap_or(0);
503 assert_eq!(control, 1); assert_eq!(ask, 1); }
506
507 #[test]
508 fn estimate_program_empty() {
509 let ir = IRProgram {
510 node_type: "Program",
511 source_line: 0, source_column: 0,
512 personas: vec![], contexts: vec![], anchors: vec![],
513 tools: vec![], memories: vec![], types: vec![],
514 flows: vec![], runs: vec![], imports: vec![],
515 agents: vec![], shields: vec![], daemons: vec![],
516 ots_specs: vec![], pix_specs: vec![], corpus_specs: vec![],
517 psyche_specs: vec![], mandate_specs: vec![],
518 lambda_data_specs: vec![], compute_specs: vec![],
519 axonstore_specs: vec![], endpoints: vec![],
520 extensions: vec![],
521 dataspace_specs: vec![],
522 resources: vec![],
523 fabrics: vec![],
524 manifests: vec![],
525 observations: vec![],
526 intention_tree: None,
527 reconciles: vec![],
528 leases: vec![],
529 ensembles: vec![],
530 sessions: vec![],
531 topologies: vec![],
532 immunes: vec![],
533 reflexes: vec![],
534 heals: vec![],
535 components: vec![],
536 views: vec![],
537 channels: vec![],
538 sockets: vec![],
539 effects: vec![],
540 };
541
542 let pricing = PricingModel::default_sonnet();
543 let report = estimate_program(&ir, &pricing);
544 assert_eq!(report.total_tokens, 0);
545 assert_eq!(report.estimated_cost_usd, 0.0);
546 assert!(report.flows.is_empty());
547 }
548
549 #[test]
550 fn estimate_program_single_flow() {
551 use crate::ir_nodes::*;
552
553 let flow = IRFlow {
554 node_type: "Flow", source_line: 1, source_column: 1,
555 name: "Analyze".into(),
556 parameters: vec![], return_type_name: "".into(),
557 return_type_generic: "".into(), return_type_optional: false,
558 steps: vec![
559 IRFlowNode::Step(IRStep {
560 node_type: "Step", source_line: 2, source_column: 1,
561 name: "gather".into(), persona_ref: "".into(),
562 given: "".into(), ask: "gather data".into(),
563 use_tool: None, probe: None, reason: None, weave: None,
564 output_type: "".into(), confidence_floor: None,
565 navigate_ref: "".into(), apply_ref: "".into(),
566 body: vec![],
567 }),
568 IRFlowNode::Reason(IRReasonStep {
569 node_type: "Reason", source_line: 3, source_column: 1,
570 strategy: "deductive".into(), target: "conclusion".into(),
571 }),
572 ],
573 edges: vec![],
574 execution_levels: vec![],
575 };
576
577 let ir = IRProgram {
578 node_type: "Program",
579 source_line: 0, source_column: 0,
580 personas: vec![], contexts: vec![], anchors: vec![],
581 tools: vec![], memories: vec![], types: vec![],
582 flows: vec![flow], runs: vec![], imports: vec![],
583 agents: vec![], shields: vec![], daemons: vec![],
584 ots_specs: vec![], pix_specs: vec![], corpus_specs: vec![],
585 psyche_specs: vec![], mandate_specs: vec![],
586 lambda_data_specs: vec![], compute_specs: vec![],
587 axonstore_specs: vec![], endpoints: vec![],
588 extensions: vec![],
589 dataspace_specs: vec![],
590 resources: vec![],
591 fabrics: vec![],
592 manifests: vec![],
593 observations: vec![],
594 intention_tree: None,
595 reconciles: vec![],
596 leases: vec![],
597 ensembles: vec![],
598 sessions: vec![],
599 topologies: vec![],
600 immunes: vec![],
601 reflexes: vec![],
602 heals: vec![],
603 components: vec![],
604 views: vec![],
605 channels: vec![],
606 sockets: vec![],
607 effects: vec![],
608 };
609
610 let pricing = PricingModel::default_sonnet();
611 let report = estimate_program(&ir, &pricing);
612
613 assert_eq!(report.flows.len(), 1);
614 assert_eq!(report.flows[0].flow_name, "Analyze");
615 assert_eq!(report.flows[0].total_steps, 2);
616
617 assert_eq!(report.total_input_tokens, 800 + 1200);
619 assert_eq!(report.total_output_tokens, 400 + 800);
620 assert_eq!(report.total_tokens, 3200);
621 assert!(report.estimated_cost_usd > 0.0);
622 }
623
624 #[test]
625 fn format_text_contains_flow_name() {
626 use crate::ir_nodes::*;
627
628 let flow = IRFlow {
629 node_type: "Flow", source_line: 1, source_column: 1,
630 name: "TestFlow".into(),
631 parameters: vec![], return_type_name: "".into(),
632 return_type_generic: "".into(), return_type_optional: false,
633 steps: vec![
634 IRFlowNode::Step(IRStep {
635 node_type: "Step", source_line: 2, source_column: 1,
636 name: "s1".into(), persona_ref: "".into(),
637 given: "".into(), ask: "do".into(),
638 use_tool: None, probe: None, reason: None, weave: None,
639 output_type: "".into(), confidence_floor: None,
640 navigate_ref: "".into(), apply_ref: "".into(),
641 body: vec![],
642 }),
643 ],
644 edges: vec![],
645 execution_levels: vec![],
646 };
647
648 let ir = IRProgram {
649 node_type: "Program", source_line: 0, source_column: 0,
650 personas: vec![], contexts: vec![], anchors: vec![],
651 tools: vec![], memories: vec![], types: vec![],
652 flows: vec![flow], runs: vec![], imports: vec![],
653 agents: vec![], shields: vec![], daemons: vec![],
654 ots_specs: vec![], pix_specs: vec![], corpus_specs: vec![],
655 psyche_specs: vec![], mandate_specs: vec![],
656 lambda_data_specs: vec![], compute_specs: vec![],
657 axonstore_specs: vec![], endpoints: vec![],
658 extensions: vec![],
659 dataspace_specs: vec![],
660 resources: vec![],
661 fabrics: vec![],
662 manifests: vec![],
663 observations: vec![],
664 intention_tree: None,
665 reconciles: vec![],
666 leases: vec![],
667 ensembles: vec![],
668 sessions: vec![],
669 topologies: vec![],
670 immunes: vec![],
671 reflexes: vec![],
672 heals: vec![],
673 components: vec![],
674 views: vec![],
675 channels: vec![],
676 sockets: vec![],
677 effects: vec![],
678 };
679
680 let pricing = PricingModel::default_sonnet();
681 let report = estimate_program(&ir, &pricing);
682 let text = format_text(&report);
683
684 assert!(text.contains("TestFlow"));
685 assert!(text.contains("claude-sonnet-4"));
686 assert!(text.contains("Estimated cost:"));
687 assert!(text.contains("$"));
688 }
689
690 #[test]
691 fn report_serializes_to_json() {
692 let pricing = PricingModel::default_sonnet();
693 let report = CostReport {
694 pricing: pricing.clone(),
695 flows: vec![],
696 total_input_tokens: 1000,
697 total_output_tokens: 500,
698 total_tokens: 1500,
699 estimated_cost_usd: pricing.compute_cost(1000, 500),
700 };
701
702 let json = serde_json::to_string(&report).unwrap();
703 assert!(json.contains("\"total_tokens\":1500"));
704 assert!(json.contains("\"claude-sonnet-4\""));
705 }
706
707 #[test]
708 fn default_estimates_nonzero_for_llm_steps() {
709 for kind in &[StepKind::Ask, StepKind::ToolCall, StepKind::Reason,
710 StepKind::Probe, StepKind::Validate, StepKind::Refine,
711 StepKind::Weave, StepKind::MultiAgent, StepKind::Cognitive] {
712 let est = default_estimate(*kind);
713 assert!(est.input_tokens > 0, "{:?} should have nonzero input", kind);
714 assert!(est.output_tokens > 0, "{:?} should have nonzero output", kind);
715 }
716 }
717
718 #[test]
719 fn control_and_parallel_zero_cost() {
720 let est_ctrl = default_estimate(StepKind::Control);
721 assert_eq!(est_ctrl.input_tokens, 0);
722 assert_eq!(est_ctrl.output_tokens, 0);
723
724 let est_par = default_estimate(StepKind::Parallel);
725 assert_eq!(est_par.input_tokens, 0);
726 assert_eq!(est_par.output_tokens, 0);
727 }
728
729 #[test]
730 fn memory_steps_low_cost() {
731 let est = default_estimate(StepKind::Memory);
732 assert!(est.input_tokens <= 200);
733 assert!(est.output_tokens <= 100);
734 }
735}