nx_caplab_compete.nx source
↩ module page · 98 lines · 3417 B
1// nx_caplab_compete.nx -- Capitalism Lab R1: multi-firm price competition.
2//
3// Builds on the R0 atom (nx_caplab_market): instead of one firm, N firms
4// set prices into ONE market. A single market willingness-to-pay (choke
5// price A) bounds demand; each firm's "appeal" is how far it undercuts the
6// choke; the market's customers split among firms in proportion to appeal
7// (cheaper -> bigger share). Allocation uses largest-remainder so the
8// split is CONSERVED: sum of firm quantities == total market customers
9// (no customers vanish or get invented), unless every firm is priced out.
10//
11// MODEL:
12// appeal_i = max(0, A - price_i) (0 if priced >= choke)
13// share_i = market_M * appeal_i / sum(appeal) (largest-remainder)
14// profit_i = (price_i - unit_cost_i) * share_i
15//
16// This is the heart of "capitalism": a lower price wins more of the market,
17// but margin x volume decides who actually earns the most.
18//
19// nx_safety_envelope:
20// intended_use: capitalism-lab competition core (pure functions, no I/O)
21// sil_target: SIL1
22// verdict: NOT_YET_EVALUATED
23//
24// genealogy_id: capitalism_lab_competition_canon
25// lineage_id: nx_caplab_compete_v1
26
27import "nx_syscalls.nx"
28import "nx_tier.nx"
29
30// A firm's appeal: how far it undercuts the market's choke price.
31func nx_clab_appeal(choke_a: nx_int, price: nx_int) -> nx_int {
32 let w: nx_int = choke_a - price
33 if w < 0 { return 0 }
34 return w
35}
36
37func nx_clab_appeal_sum(n: nx_int, prices: *i64, choke_a: nx_int) -> nx_int {
38 var s: nx_int = 0
39 var i: nx_int = 0
40 while i < n {
41 s = s + nx_clab_appeal(choke_a, prices[i])
42 i = i + 1
43 }
44 return s
45}
46
47// Split market_m customers among n firms by appeal, conserving the total
48// via largest-remainder (Hamilton) apportionment: ties break to the lowest
49// index for determinism. If every firm is priced out (sum appeal == 0),
50// nobody buys and all quantities are 0.
51func nx_clab_split(n: nx_int, prices: *i64, choke_a: nx_int,
52 market_m: nx_int, out_q: *i64) {
53 let total_w: nx_int = nx_clab_appeal_sum(n, prices, choke_a)
54 var i: nx_int = 0
55 if total_w <= 0 {
56 while i < n { out_q[i] = 0; i = i + 1 }
57 return
58 }
59
60 let rem: *i64 = (sys_mmap(n * NX_SIZEOF_NX_INT)) as *i64
61 let got: *i64 = (sys_mmap(n * NX_SIZEOF_NX_INT)) as *i64
62 var allocated: nx_int = 0
63 i = 0
64 while i < n {
65 let w: nx_int = nx_clab_appeal(choke_a, prices[i])
66 let num: nx_int = market_m * w
67 out_q[i] = num / total_w
68 rem[i] = num % total_w
69 got[i] = 0
70 allocated = allocated + out_q[i]
71 i = i + 1
72 }
73
74 // Distribute the leftover (== sum of dropped fractions, always < n) one
75 // unit each to the firms with the largest remainder.
76 var leftover: nx_int = market_m - allocated
77 while leftover > 0 {
78 var best: nx_int = -1
79 var best_rem: nx_int = -1
80 var j: nx_int = 0
81 while j < n {
82 if got[j] == 0 {
83 if rem[j] > best_rem {
84 best_rem = rem[j]
85 best = j
86 }
87 }
88 j = j + 1
89 }
90 out_q[best] = out_q[best] + 1
91 got[best] = 1
92 leftover = leftover - 1
93 }
94}
95
96func nx_clab_firm_profit(price: nx_int, unit_cost: nx_int, qty: nx_int) -> nx_int {
97 return (price - unit_cost) * qty
98}