code wiki / _hdl_build / nx_price.nx

nx_price.nx source

↩ module page · 284 lines · 12790 B

1// nx_price.nx -- LIB: PRICE TRACKING across stores (R-PRICE, pillar 3 of seed-to-feed). Operator: "the tracking 2// of prices but stores in the area for stores across america and how they are doing as far as prices." A 3// sovereign price registry (knowledge/store/price-*, NO TSV, integer cents no-float): stores with an AREA, and 4// date-stamped price OBSERVATIONS per item. "How a store is doing" = a measured competitiveness INDEX vs the 5// area median (100 = exactly the area median; <100 = cheaper than median = better for the shopper). Finds the 6// cheapest store / basket per area = where to buy for least (the affordability arc). 7// 8// HONEST: prices are RECORDED observations with a date + source (rule 23), NOT live-scraped or claimed real-time; 9// the index is computed from those recorded numbers. Ties to [[project-nishi-food-branch-2026-06-22]] affordability. 10// license_tier: ORIGINAL 11import "nx_food_science.nx" 12import "nx_seg_store.nx" 13import "nx_syscalls.nx" 14 15func pr_put(prefix: *u8, key: *u8, val: *u8) -> i64 { 16 let vl: i64 = as_len(val) 17 if fd_streq_store(prefix, key, val, vl) == 1 { return 0 } 18 let w: *i64 = ss_begin() 19 ss_add(w, 1, key, val, vl) 20 let seg: i64 = fd_seg_next(prefix) 21 ss_commit(prefix, w, seg) 22 return 1 23} 24 25// split a TAB list into NUL-terminated tokens; returns count. 26func pr_split(buf: *u8, len: i64, outtoks: *i64) -> i64 { 27 var c: i64 = 0 28 var i: i64 = 0 29 var ls: i64 = 0 30 while i <= len { 31 var sep: i64 = 0 32 if i == len { sep = 1 } else { if buf[i] == (9 as u8) { sep = 1 } } 33 if sep == 1 { 34 let tl: i64 = i - ls 35 if tl > 0 { 36 let tok: *u8 = sys_mmap(48) 37 var t: i64 = 0 38 while t < tl { tok[t] = buf[ls + t]; t = t + 1 } tok[tl] = 0 as u8 39 outtoks[c] = tok as i64; c = c + 1 40 } 41 ls = i + 1 42 } 43 i = i + 1 44 } 45 return c 46} 47 48// idempotent append of `token` to the TAB list stored at `key`. 49func pr_list_append(prefix: *u8, key: *u8, token: *u8) -> i64 { 50 let pq: *i64 = sys_mmap(16) as *i64 51 let lq: *i64 = sys_mmap(16) as *i64 52 let cur: *u8 = sys_mmap(2048) 53 var o: i64 = 0 54 if ss_get(prefix, key, pq, lq) == 1 { 55 let b: *u8 = pq[0] as *u8 56 let n: i64 = lq[0] 57 // already present? scan tokens 58 let toks: *i64 = sys_mmap(8 * 256) as *i64 59 let nt: i64 = pr_split(b, n, toks) 60 var i: i64 = 0 61 while i < nt { if fd_streq(toks[i] as *u8, token) == 1 { return 0 } i = i + 1 } 62 var u: i64 = 0 63 while u < n { cur[u] = b[u]; u = u + 1 } o = n 64 cur[o] = 9 as u8; o = o + 1 65 } 66 o = as_append(cur, o, token) 67 cur[o] = 0 as u8 68 return pr_put(prefix, key, cur) 69} 70 71// ---- stores + areas ---- 72func pr_store_key(sid: *u8, out: *u8) -> i64 { var o: i64 = 0; o = as_append(out, o, "price:store:" as *u8); o = as_append(out, o, sid); out[o] = 0 as u8; return o } 73func pr_area_key(area: *u8, out: *u8) -> i64 { var o: i64 = 0; o = as_append(out, o, "price:area:" as *u8); o = as_append(out, o, area); o = as_append(out, o, ":stores" as *u8); out[o] = 0 as u8; return o } 74func pr_obs_key(sid: *u8, item: *u8, out: *u8) -> i64 { 75 var o: i64 = 0 76 o = as_append(out, o, "price:obs:" as *u8); o = as_append(out, o, sid) 77 out[o] = 58 as u8; o = o + 1 78 o = as_append(out, o, item); out[o] = 0 as u8; return o 79} 80 81func pr_add_store(prefix: *u8, sid: *u8, name: *u8, chain: *u8, area: *u8, state: *u8, cite: *u8) -> i64 { 82 let key: *u8 = sys_mmap(96); pr_store_key(sid, key) 83 let val: *u8 = sys_mmap(256) 84 var o: i64 = 0 85 o = as_append(val, o, name); val[o] = 9 as u8; o = o + 1 86 o = as_append(val, o, chain); val[o] = 9 as u8; o = o + 1 87 o = as_append(val, o, area); val[o] = 9 as u8; o = o + 1 88 o = as_append(val, o, state); val[o] = 9 as u8; o = o + 1 89 o = as_append(val, o, cite) 90 val[o] = 0 as u8 91 let w: i64 = pr_put(prefix, key, val) 92 let ak: *u8 = sys_mmap(96); pr_area_key(area, ak) 93 pr_list_append(prefix, ak, sid) 94 return w 95} 96 97func pr_store_field(prefix: *u8, sid: *u8, f: i64, out: *u8) -> i64 { 98 let key: *u8 = sys_mmap(96); pr_store_key(sid, key) 99 let pq: *i64 = sys_mmap(16) as *i64 100 let lq: *i64 = sys_mmap(16) as *i64 101 if ss_get(prefix, key, pq, lq) != 1 { out[0] = 0 as u8; return 0 } 102 return fd_field(pq[0] as *u8, lq[0], f, out) 103} 104 105// record a date-stamped price observation (cents). Latest write wins (R0 keeps the current observation). 106func pr_set_price(prefix: *u8, sid: *u8, item: *u8, cents: i64, date: *u8, unit: *u8, source: *u8) -> i64 { 107 let key: *u8 = sys_mmap(96); pr_obs_key(sid, item, key) 108 let val: *u8 = sys_mmap(256) 109 var o: i64 = 0 110 o = fd_apnum(val, o, cents); val[o] = 9 as u8; o = o + 1 111 o = as_append(val, o, date); val[o] = 9 as u8; o = o + 1 112 o = as_append(val, o, unit); val[o] = 9 as u8; o = o + 1 113 o = as_append(val, o, source) 114 val[o] = 0 as u8 115 return pr_put(prefix, key, val) 116} 117// price in cents, or -1 if no observation recorded for (sid,item). 118func pr_get_price(prefix: *u8, sid: *u8, item: *u8) -> i64 { 119 let key: *u8 = sys_mmap(96); pr_obs_key(sid, item, key) 120 let pq: *i64 = sys_mmap(16) as *i64 121 let lq: *i64 = sys_mmap(16) as *i64 122 if ss_get(prefix, key, pq, lq) != 1 { return 0 - 1 } 123 let fb: *u8 = sys_mmap(24) 124 let fl: i64 = fd_field(pq[0] as *u8, lq[0], 0, fb) 125 return fd_atoi(fb, fl) 126} 127 128// a string field of a price observation (1=date, 2=unit, 3=source) -- proves recorded provenance. 129func pr_obs_field(prefix: *u8, sid: *u8, item: *u8, f: i64, out: *u8) -> i64 { 130 let key: *u8 = sys_mmap(96); pr_obs_key(sid, item, key) 131 let pq: *i64 = sys_mmap(16) as *i64 132 let lq: *i64 = sys_mmap(16) as *i64 133 if ss_get(prefix, key, pq, lq) != 1 { out[0] = 0 as u8; return 0 } 134 return fd_field(pq[0] as *u8, lq[0], f, out) 135} 136 137// the sids in an area; returns count. 138func pr_area_stores(prefix: *u8, area: *u8, outsids: *i64) -> i64 { 139 let ak: *u8 = sys_mmap(96); pr_area_key(area, ak) 140 let pq: *i64 = sys_mmap(16) as *i64 141 let lq: *i64 = sys_mmap(16) as *i64 142 if ss_get(prefix, ak, pq, lq) != 1 { return 0 } 143 return pr_split(pq[0] as *u8, lq[0], outsids) 144} 145 146// collect the prices for `item` across an area's stores into outvals; returns count of stores that have it. 147func pr_area_prices(prefix: *u8, area: *u8, item: *u8, outvals: *i64) -> i64 { 148 let sids: *i64 = sys_mmap(8 * 256) as *i64 149 let ns: i64 = pr_area_stores(prefix, area, sids) 150 var c: i64 = 0 151 var i: i64 = 0 152 while i < ns { 153 let p: i64 = pr_get_price(prefix, sids[i] as *u8, item) 154 if p >= 0 { outvals[c] = p; c = c + 1 } 155 i = i + 1 156 } 157 return c 158} 159 160// integer median of an area's prices for an item; -1 if none. (Insertion sort; lower-middle for even counts.) 161func pr_median(prefix: *u8, area: *u8, item: *u8) -> i64 { 162 let v: *i64 = sys_mmap(8 * 256) as *i64 163 let n: i64 = pr_area_prices(prefix, area, item, v) 164 if n == 0 { return 0 - 1 } 165 var i: i64 = 1 166 while i < n { 167 let key: i64 = v[i] 168 var j: i64 = i - 1 169 var go: i64 = 1 170 while go == 1 { 171 if j < 0 { go = 0 } else { 172 if v[j] > key { v[j + 1] = v[j]; j = j - 1 } else { go = 0 } 173 } 174 } 175 v[j + 1] = key 176 i = i + 1 177 } 178 return v[n / 2] 179} 180// cheapest store's price for an item in an area, with its sid via out_sid; -1 if none. 181func pr_cheapest(prefix: *u8, area: *u8, item: *u8, out_sid: *u8) -> i64 { 182 let sids: *i64 = sys_mmap(8 * 256) as *i64 183 let ns: i64 = pr_area_stores(prefix, area, sids) 184 var best: i64 = 0 - 1 185 var bsid: *u8 = "-" as *u8 186 var i: i64 = 0 187 while i < ns { 188 let p: i64 = pr_get_price(prefix, sids[i] as *u8, item) 189 if p >= 0 { 190 var take: i64 = 0 191 if best < 0 { take = 1 } else { if p < best { take = 1 } } 192 if take == 1 { best = p; bsid = sids[i] as *u8 } 193 } 194 i = i + 1 195 } 196 var u: i64 = 0 197 while bsid[u] != (0 as u8) { out_sid[u] = bsid[u]; u = u + 1 } out_sid[u] = 0 as u8 198 return best 199} 200 201// a store's basket total (cents) over items[0..nitems); skips items it doesn't stock. 202func pr_basket(prefix: *u8, sid: *u8, items: *i64, nitems: i64) -> i64 { 203 var s: i64 = 0 204 var i: i64 = 0 205 while i < nitems { 206 let p: i64 = pr_get_price(prefix, sid, items[i] as *u8) 207 if p >= 0 { s = s + p } 208 i = i + 1 209 } 210 return s 211} 212 213// competitiveness INDEX of a store vs its area's medians (avg over the basket of price*100/median). 100 = at the 214// area median; <100 = cheaper than median (doing well for shoppers); >100 = premium. -1 if no comparable items. 215func pr_store_index(prefix: *u8, area: *u8, sid: *u8, items: *i64, nitems: i64) -> i64 { 216 var sum: i64 = 0 217 var cnt: i64 = 0 218 var i: i64 = 0 219 while i < nitems { 220 let p: i64 = pr_get_price(prefix, sid, items[i] as *u8) 221 let med: i64 = pr_median(prefix, area, items[i] as *u8) 222 if p >= 0 { if med > 0 { sum = sum + (p * 100) / med; cnt = cnt + 1 } } 223 i = i + 1 224 } 225 if cnt == 0 { return 0 - 1 } 226 return sum / cnt 227} 228 229// render the area price comparison: stores sorted cheapest-basket first, with basket total + competitiveness index. 230func pr_render_area(prefix: *u8, area: *u8, items: *i64, nitems: i64, out: *u8) -> i64 { 231 let sids: *i64 = sys_mmap(8 * 256) as *i64 232 let ns: i64 = pr_area_stores(prefix, area, sids) 233 // basket per store, then insertion-sort sids by basket ascending 234 let bask: *i64 = sys_mmap(8 * 256) as *i64 235 var i: i64 = 0 236 while i < ns { bask[i] = pr_basket(prefix, sids[i] as *u8, items, nitems); i = i + 1 } 237 i = 1 238 while i < ns { 239 let kb: i64 = bask[i] 240 let ks: i64 = sids[i] 241 var j: i64 = i - 1 242 var go: i64 = 1 243 while go == 1 { 244 if j < 0 { go = 0 } else { 245 if bask[j] > kb { bask[j + 1] = bask[j]; sids[j + 1] = sids[j]; j = j - 1 } else { go = 0 } 246 } 247 } 248 bask[j + 1] = kb; sids[j + 1] = ks 249 i = i + 1 250 } 251 var o: i64 = 0 252 o = as_append(out, o, "<!doctype html><html lang='en'><head><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1'><title>Nishi price tracker</title><style>body{margin:0;font-family:system-ui,sans-serif;color:#1b2330;background:#eef2f7;line-height:1.5}header{background:#1f3a5f;color:#fff;padding:22px}header h1{margin:0;font-size:1.4rem}header p{margin:4px 0 0;color:#cfe0f5}main{max-width:680px;margin:0 auto;padding:16px}table{border-collapse:collapse;width:100%;background:#fff;border-radius:12px;overflow:hidden}th,td{padding:9px 12px;text-align:left;border-top:1px solid #eef3f8}th{background:#e4ecf6;color:#27405f}td.n{text-align:right;font-variant-numeric:tabular-nums}.best{background:#e6f3e2}.good{color:#1f7a44;font-weight:700}.hi{color:#a23a2a;font-weight:700}.muted{color:#5a6b80;font-size:.88rem}</style></head><body><header><h1>Price tracker &mdash; " as *u8) 253 o = as_append_escaped(out, o, area, as_len(area)) 254 o = as_append(out, o, "</h1><p>Who's cheapest on the basket, and how each store is doing vs the local median.</p></header><main><table><tr><th>Store</th><th>Basket</th><th>Index (100=median)</th></tr>" as *u8) 255 i = 0 256 while i < ns { 257 let sid: *u8 = sids[i] as *u8 258 let nm: *u8 = sys_mmap(96); pr_store_field(prefix, sid, 0, nm) 259 let idx: i64 = pr_store_index(prefix, area, sid, items, nitems) 260 if i == 0 { o = as_append(out, o, "<tr class='best'><td>" as *u8) } else { o = as_append(out, o, "<tr><td>" as *u8) } 261 o = as_append_escaped(out, o, nm, as_len(nm)) 262 o = as_append(out, o, "</td><td class='n'>" as *u8); o = im_money_local(out, o, bask[i]) 263 o = as_append(out, o, "</td><td class='n " as *u8) 264 if idx <= 100 { o = as_append(out, o, "good'>" as *u8) } else { o = as_append(out, o, "hi'>" as *u8) } 265 o = fd_apnum(out, o, idx) 266 o = as_append(out, o, "</td></tr>" as *u8) 267 i = i + 1 268 } 269 o = as_append(out, o, "</table><p class='muted'>Prices are recorded, date-stamped observations with a source &mdash; not live-scraped or guaranteed current. Index = average of price&divide;area-median across the basket.</p></main></body></html>" as *u8) 270 out[o] = 0 as u8 271 return o 272} 273 274// local money formatter ($d.cc) so this module doesn't depend on nx_impact. 275func im_money_local(dst: *u8, off: i64, cents: i64) -> i64 { 276 var o: i64 = off 277 dst[o] = 36 as u8; o = o + 1 278 o = fd_apnum(dst, o, cents / 100) 279 dst[o] = 46 as u8; o = o + 1 280 let r: i64 = cents % 100 281 dst[o] = (48 + (r / 10)) as u8; o = o + 1 282 dst[o] = (48 + (r % 10)) as u8; o = o + 1 283 return o 284}