nx_arxiv_census.nx source
↩ module page · 132 lines · 6371 B
1// nx_arxiv_census.nx -- COMPLETE-POPULATION CENSUS of an arXiv date window. Turns "I read the current
2// month" from a seat's assertion into a MECHANICAL verdict, which is what the sotabar/barscan law needs.
3//
4// COMPOSES the incumbents, deliberately does not duplicate them:
5// se_fetch (nx_search_extern) -- polite sovereign-TLS fetch, the ONE arXiv fetcher
6// em_int_after (nx_search_emerge) -- opensearch:totalResults reader
7// em_collect_yms (nx_search_emerge) -- <published> year-month extractor
8// em_maxym (nx_search_emerge) -- newest YYYY-MM, which IS the barscan newest= field
9// ac_reason/ac_verdict (nx_arxiv_census_lib) -- the ONE decision, shared with the gate in-process
10//
11// The ONE question neither incumbent asks: does opensearch:totalResults RECONCILE against the number of
12// entries actually delivered? Equal means the window is a POPULATION and an absence claim over it is sound;
13// fewer means a SAMPLE wearing a population's clothes and absence over it is UNPROVEN.
14//
15// nx_search_emerge is NOT defective and this organ does not replace it: it samples 12 on purpose for a cheap
16// daily velocity signal and HONESTLY prints "sampled hits" beside "total volume". That is a different
17// question (is this field moving) from this one (what did this window actually contain). Its em_collect_yms
18// cap is silent at the FUNCTION level for any future composer, which is filed as dataio rung DI19.
19// license_tier: ORIGINAL
20import "nx_search_extern.nx"
21import "nx_search_emerge.nx"
22import "nx_arxiv_census_lib.nx"
23import "nx_syscalls.nx"
24import "nx_x509_trust_store.nx"
25import "nx_trust_store_load_from_certdata.nx"
26
27const AC_BUFCAP: i64 = 4194304
28const AC_URLCAP: i64 = 1024
29const AC_MAXD: i64 = 4096 // date slots. nd==AC_MAXD is ANNOUNCED as a local cap, never silent.
30const AC_YMSLOT: i64 = 8
31const AC_TRUST_HINT: i64 = 512
32const AC_DEFAULT_MAX: i64 = 300
33const AC_MINARGC: i64 = 4
34const AC_ASCII_ZERO: i64 = 48
35const AC_ASCII_NINE: i64 = 57
36const AC_NUMBUF: i64 = 28
37
38func acp(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
39func acn(v: i64) -> i64 {
40 if v==0 { sys_write(1,"0" as *u8,1); return 0 }
41 var m: i64=v
42 if m<0 { sys_write(1,"-" as *u8,1); m=0-m }
43 let t: *u8=sys_mmap(AC_NUMBUF); var k: i64=0
44 while m>0 { t[k]=(AC_ASCII_ZERO+(m%10)) as u8; m=m/10; k=k+1 }
45 while k>0 { k=k-1; sys_write(1,(((t as i64)+k) as *u8),1) }
46 return 0
47}
48func ac_numto(dst: *u8, off: i64, v: i64) -> i64 {
49 var o: i64=off
50 if v==0 { dst[o]=AC_ASCII_ZERO as u8; return o+1 }
51 var m: i64=v
52 let t: *u8=sys_mmap(AC_NUMBUF); var k: i64=0
53 while m>0 { t[k]=(AC_ASCII_ZERO+(m%10)) as u8; m=m/10; k=k+1 }
54 while k>0 { k=k-1; dst[o]=t[k]; o=o+1 }
55 return o
56}
57func ac_atoi(s: *u8) -> i64 {
58 var v: i64=0; var i: i64=0; var seen: i64=0
59 while s[i]!=(0 as u8) {
60 let c: i64=s[i] as i64
61 if c>=AC_ASCII_ZERO { if c<=AC_ASCII_NINE { v=v*10+(c-AC_ASCII_ZERO); seen=1 } }
62 i=i+1
63 }
64 if seen==0 { return 0-1 }
65 return v
66}
67
68// the trust store lives at data/ from the estate root and ../data/ from buildroot; a CWD must not decide a verdict
69func ac_trust() -> i64 {
70 let a: i64=nx_trust_store_load_from_certdata("data/mozilla_certdata.txt" as *u8, AC_TRUST_HINT, AC_BUFCAP)
71 if a>0 { return a }
72 return nx_trust_store_load_from_certdata("../data/mozilla_certdata.txt" as *u8, AC_TRUST_HINT, AC_BUFCAP)
73}
74
75func ac_usage() -> i64 {
76 acp("usage: nx_arxiv_census <category> <YYYYMMDD-from> <YYYYMMDD-to> [max_results]\n" as *u8)
77 acp(" e.g. nx_arxiv_census cs.SE 20260901 20260907\n" as *u8)
78 acp(" exit 0 COMPLETE (delivered==totalResults) | 1 CAPPED | 2 usage | 3 UNREADABLE\n" as *u8)
79 return 0
80}
81
82func main(argc: i64, argv: *i64) -> i64 {
83 if argc<AC_MINARGC { ac_usage(); sys_exit(ACV_USAGE); return ACV_USAGE }
84 let cat: *u8=argv[1] as *u8
85 let dfrom: *u8=argv[2] as *u8
86 let dto: *u8=argv[3] as *u8
87 var maxr: i64=AC_DEFAULT_MAX
88 if argc>AC_MINARGC { let m: i64=ac_atoi(argv[AC_MINARGC] as *u8); if m>0 { maxr=m } }
89
90 let tr: i64=ac_trust()
91 if tr<=0 { acp("AC-REASON trust store load FAILED from data/ and ../data/\n" as *u8); acp("verdict=UNREADABLE\n" as *u8); sys_exit(ACV_UNREADABLE); return ACV_UNREADABLE }
92 let store: *TrustStore=tr as *TrustStore
93
94 let url: *u8=sys_mmap(AC_URLCAP); var o: i64=0
95 o=se_app(url,o,"https://export.arxiv.org/api/query?search_query=cat:" as *u8)
96 o=se_app(url,o,cat)
97 o=se_app(url,o,"+AND+submittedDate:[" as *u8)
98 o=se_app(url,o,dfrom); o=se_app(url,o,"0000+TO+" as *u8); o=se_app(url,o,dto); o=se_app(url,o,"0000]" as *u8)
99 o=se_app(url,o,"&sortBy=submittedDate&sortOrder=descending&max_results=" as *u8)
100 o=ac_numto(url,o,maxr)
101 url[o]=0 as u8
102
103 acp("AC-QUERY " as *u8); sys_write(1,url,o); acp("\n" as *u8)
104
105 let buf: *u8=sys_mmap(AC_BUFCAP)
106 let n: i64=se_fetch(store, url, buf, AC_BUFCAP, "arXiv" as *u8)
107 if n<=0 { acp("AC-REASON fetch returned no body\n" as *u8); acp("verdict=UNREADABLE\n" as *u8); sys_exit(ACV_UNREADABLE); return ACV_UNREADABLE }
108
109 let total: i64=em_int_after(buf, n, "opensearch:totalResults>" as *u8)
110 let flat: *u8=sys_mmap(AC_MAXD*AC_YMSLOT)
111 let nd: i64=em_collect_yms(buf, n, "<published>" as *u8, flat, AC_MAXD)
112 let newest: *u8=sys_mmap(AC_YMSLOT)
113 em_maxym(flat, nd, newest)
114 let recent: i64=em_count_eq(flat, nd, newest)
115
116 let reason: i64=ac_reason(total, nd, AC_MAXD)
117 let verdict: i64=ac_verdict_of_reason(reason)
118
119 acp("category=" as *u8); acp(cat); acp(" from=" as *u8); acp(dfrom); acp(" to=" as *u8); acp(dto); acp("\n" as *u8)
120 acp("body_bytes=" as *u8); acn(n); acp("\n" as *u8)
121 acp("max_results=" as *u8); acn(maxr); acp("\n" as *u8)
122 acp("totalResults=" as *u8); acn(total); acp("\n" as *u8)
123 acp("delivered=" as *u8); acn(nd); acp("\n" as *u8)
124 acp("newest_ym=" as *u8); acp(newest); acp("\n" as *u8)
125 acp("newest_ym_count=" as *u8); acn(recent); acp("\n" as *u8)
126 acp("local_slot_cap=" as *u8); acn(AC_MAXD); acp("\n" as *u8)
127 acp("reason_code=" as *u8); acn(reason); acp("\n" as *u8)
128 acp("AC-REASON " as *u8); acp(ac_reason_text(reason)); acp("\n" as *u8)
129 acp("verdict=" as *u8); acp(ac_verdict_text(verdict)); acp("\n" as *u8)
130 sys_exit(verdict)
131 return verdict
132}