eyeling 1.14.0 → 1.14.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/HANDBOOK.md CHANGED
@@ -969,7 +969,9 @@ If the types don’t fit any supported case, the builtin fails.
969
969
 
970
970
  **Shape:** `( $base $exp ) math:exponentiation $result`
971
971
 
972
- - Forward direction: if base and exponent are numeric, computes `base ** exp`.
972
+ - Forward direction supports two modes:
973
+ - **Exact integer mode (BigInt):** if `$base` and `$exp` are integer literals and `$exp >= 0`, Eyeling computes the exact integer power using BigInt (with a safety cap on the estimated result size to avoid OOM).
974
+ - **Numeric mode (Number):** otherwise, if base and exponent parse as finite Numbers, computes `base ** exp`.
973
975
  - Reverse direction (limited): Eyeling can sometimes solve for the exponent if:
974
976
  - base and result are numeric, finite, and **positive**
975
977
  - base is not 1
@@ -977,6 +979,16 @@ If the types don’t fit any supported case, the builtin fails.
977
979
 
978
980
  This is a pragmatic inversion, not a full algebra system.
979
981
 
982
+ #### `math:bigExponentiation`
983
+
984
+ **Shape:** `( $base $exp ) math:bigExponentiation $result`
985
+
986
+ - Exact integer exponentiation only.
987
+ - Requires integer `$base` and non-negative integer `$exp`.
988
+ - Fails if the estimated output size exceeds Eyeling’s built-in safety cap.
989
+
990
+ This builtin exists to avoid rule-level “repeat multiply” derivations that can explode memory for large exponents (e.g., the Ackermann example).
991
+
980
992
  #### Unary “math relations” (often invertible)
981
993
 
982
994
  Eyeling implements these as a shared pattern: if the subject is numeric, compute object; else if the object is numeric, compute subject via an inverse function; if both sides are unbound, succeed once (don’t enumerate).
@@ -1551,6 +1563,12 @@ If `i` is out of range, `out` is the original string.
1551
1563
 
1552
1564
  Returns the number of differing positions between `a` and `b`. Fails if the two strings have different lengths.
1553
1565
 
1566
+ #### `string:mutateSelectBest`
1567
+
1568
+ **Shape:** `( samples current target mutProb seedState ) string:mutateSelectBest ( best bestScore seedState2 )`
1569
+
1570
+ Generates `samples` mutated variants of `current` (mutating each character with probability `mutProb` percent), scores each candidate against `target` (Hamming distance), and returns the best candidate (lowest score). RNG is a deterministic 31-bit LCG, threaded via `seedState` → `seedState2`, so runs are reproducible.
1571
+
1554
1572
  ### Containment and prefix/suffix tests
1555
1573
 
1556
1574
  - `string:contains`
@@ -1976,24 +1994,6 @@ References:
1976
1994
 
1977
1995
  If you are running untrusted inputs, consider `--super-restricted` to disable all builtins except implication.
1978
1996
 
1979
- ### Eyeling fast-path builtins
1980
-
1981
- Eyeling ships one optional fast-path builtins builtin e.g. used by the genetic-algorithm example:
1982
-
1983
- #### `urn:eyeling:ga:solveString`
1984
-
1985
- **Shape:** `( target mutationProbability samples seed traceEvery maxGenerations ) urn:eyeling:ga:solveString ( generation score value seed )`
1986
-
1987
- - `target` and `value` are `xsd:string` literals.
1988
- - `mutationProbability` is interpreted as a percentage per character (0–100).
1989
- - `traceEvery` controls debug tracing:
1990
- - `0` disables tracing,
1991
- - `1` traces every generation,
1992
- - `N` traces every `N` generations.
1993
- - `maxGenerations` is a safety cap:
1994
- - `0` means unlimited,
1995
- - otherwise the builtin stops once `generation >= maxGenerations` (even if not solved).
1996
-
1997
1997
  ### A.6 Skolemization and `log:skolem`
1998
1998
 
1999
1999
  When forward rule heads contain blank nodes (existentials), Eyeling replaces them with generated Skolem IRIs so derived facts are ground.
@@ -39,17 +39,24 @@
39
39
  (?Y ?Z) math:product ?A.
40
40
  }.
41
41
 
42
+ # exponentiation (x=3) — use BigInt exponentiation builtin to avoid blow-up
43
+ {
44
+ (3 ?Y ?Z) :ackermann ?A.
45
+ } <= {
46
+ (?Z ?Y) math:bigExponentiation ?A.
47
+ }.
48
+
42
49
  # exponentiation (x=3), tetration (x=4), pentation (x=5), hexation (x=6), etc
43
50
  {
44
51
  (?X 0 ?Z) :ackermann 1.
45
52
  } <= {
46
- ?X math:greaterThan 2.
53
+ ?X math:greaterThan 3.
47
54
  }.
48
55
 
49
56
  {
50
57
  (?X ?Y ?Z) :ackermann ?A.
51
58
  } <= {
52
- ?X math:greaterThan 2.
59
+ ?X math:greaterThan 3.
53
60
  ?Y math:notEqualTo 0.
54
61
  (?Y 1) math:difference ?B.
55
62
  (?X ?B ?Z) :ackermann ?C.
@@ -69,7 +76,7 @@
69
76
  (3 1000) :ackermann ?A7.
70
77
  (4 0) :ackermann ?A8.
71
78
  (4 1) :ackermann ?A9.
72
- #(4 2) :ackermann ?A10.
79
+ (4 2) :ackermann ?A10.
73
80
  (5 0) :ackermann ?A11.
74
81
  } => {
75
82
  :test :is {
@@ -83,7 +90,7 @@
83
90
  (3 1000) :ackermann ?A7.
84
91
  (4 0) :ackermann ?A8.
85
92
  (4 1) :ackermann ?A9.
86
- #(4 2) :ackermann ?A10.
93
+ (4 2) :ackermann ?A10.
87
94
  (5 0) :ackermann ?A11.
88
95
  }.
89
96
  }.
@@ -10,46 +10,158 @@
10
10
  # This is a simple hill-climbing GA (selection + mutation, no crossover), using a seeded RNG
11
11
  # so runs are reproducible.
12
12
  #
13
- # Uses Eyeling's builtin:
14
- # urn:eyeling:ga:solveString
13
+ # The GA is still expressed as backward rules (<=) for the outer evolution loop,
14
+ # but the expensive inner loop ("take N mutated samples and keep the best") is
15
+ # handled by a single string builtin to avoid huge proof trees and OOM.
15
16
  #
16
- # Configure:
17
- # :mutationProbability 5 # percent per character
18
- # :samples 80
19
- # :seed 100
20
- # :traceEvery 0 # 0=off, 1=every generation, 100=every 100 generations
21
- # :maxGenerations 0 # 0=unlimited
22
17
  # ==========================================================================================
23
18
 
24
- @prefix : <http://example.org/ga-solve#>.
25
- @prefix ega: <urn:eyeling:ga:>.
19
+ @prefix : <urn:ga:>.
26
20
  @prefix log: <http://www.w3.org/2000/10/swap/log#>.
21
+ @prefix math: <http://www.w3.org/2000/10/swap/math#>.
22
+ @prefix list: <http://www.w3.org/2000/10/swap/list#>.
27
23
  @prefix string: <http://www.w3.org/2000/10/swap/string#>.
28
24
 
29
25
  :cfg
30
26
  :mutationProbability 5;
31
27
  :samples 80;
32
28
  :seed 100;
33
- :traceEvery 1;
34
- :maxGenerations 0.
35
29
 
36
- # solveResult(TargetString) = (generation score value seed)
37
- { ?TargetString :solveResult ( ?Gen ?Score ?Value ?Seed ) } <= {
30
+ # Debug knobs:
31
+ :traceEvery 1; # 0 disables; 1 traces every generation
32
+ :maxGenerations 0; # 0 means unlimited
33
+
34
+ :alphabet (" " "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M"
35
+ "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z").
36
+
37
+ # ------------------------
38
+ # Small arithmetic helpers
39
+ # ------------------------
40
+
41
+ { ( ?N 1 ) :dec ?N1 } <= { ( ?N 1 ) math:difference ?N1 }.
42
+ { ( ?N 1 ) :inc ?N1 } <= { ( ?N 1 ) math:sum ?N1 }.
43
+
44
+ # ----------------------------------------------
45
+ # Seeded RNG (LCG) and JS Math.round(rnd() * N)
46
+ # (Used only for the initial random chromosome.)
47
+ # ----------------------------------------------
48
+
49
+ { ?S0 :lcgNext ?S1 } <= {
50
+ ( ?S0 1103515245 ) math:product ?P .
51
+ ( ?P 12345 ) math:sum ?T .
52
+ ( ?T 2147483648 ) math:remainder ?S1
53
+ }.
54
+
55
+ { ( ?S0 ?N ) :randRound ( ?R ?S1 ) } <= {
56
+ ?S0 :lcgNext ?S1 .
57
+ ( ?S1 ?N ) math:product ?A .
58
+ ( ?A ?A ) math:sum ?TwoA .
59
+ ( ?TwoA 2147483648 ) math:sum ?Num .
60
+ ( 2147483648 2 ) math:product ?Den .
61
+ ( ?Num ?Den ) math:integerQuotient ?R
62
+ }.
63
+
64
+ { ?S0 :randomAlpha ( ?Ch ?S1 ) } <= {
65
+ :cfg :alphabet ?Alphabet .
66
+ ( ?S0 26 ) :randRound ( ?Idx ?S1 ) .
67
+ ( ?Alphabet ?Idx ) list:memberAt ?Ch
68
+ }.
69
+
70
+ { ( 0 ?S0 ) :randomText ( "" ?S0 ) } <= true.
71
+
72
+ { ( ?Len ?S0 ) :randomText ( ?Out ?S2 ) } <= {
73
+ ?Len math:greaterThan 0 .
74
+ ( ?Len 1 ) :dec ?Len1 .
75
+ ?S0 :randomAlpha ( ?Ch ?S1 ) .
76
+ ( ?Len1 ?S1 ) :randomText ( ?Rest ?S2 ) .
77
+ ( "%s%s" ?Ch ?Rest ) string:format ?Out
78
+ }.
79
+
80
+ # -------------------------
81
+ # tracing helper (optional)
82
+ # -------------------------
83
+
84
+ { ( 0 ?Gen ?Score ?Value ) :traceMaybe true } <= true.
85
+
86
+ { ( ?Every ?Gen ?Score ?Value ) :traceMaybe true } <= {
87
+ ?Every math:greaterThan 0 .
88
+ ( ?Gen ?Every ) math:remainder ?R .
89
+ ?R math:equalTo 0 .
90
+ ?Gen log:trace ( ?Score ?Value )
91
+ }.
92
+
93
+ # --------------
94
+ # evolution loop
95
+ # --------------
96
+
97
+ # Stop when score == 0
98
+ { ( ?Current 0 ?Target ?MutProb ?Samples ?Gen ?S0 ?MaxGen ?TraceEvery )
99
+ :evolve ( ?Current ?Gen ?S0 ) } <= true.
100
+
101
+ # Stop when generation cap is reached (only if MaxGen > 0)
102
+ { ( ?Current ?Score ?Target ?MutProb ?Samples ?Gen ?S0 ?MaxGen ?TraceEvery )
103
+ :evolve ( ?Current ?Gen ?S0 ) } <= {
104
+ ?Score math:notEqualTo 0 .
105
+ ?MaxGen math:greaterThan 0 .
106
+ ?Gen math:equalTo ?MaxGen
107
+ }.
108
+
109
+ # Unlimited run (MaxGen == 0): keep evolving
110
+ { ( ?Current ?Score ?Target ?MutProb ?Samples ?Gen ?S0 0 ?TraceEvery )
111
+ :evolve ( ?Final ?FinalGen ?S2 ) } <= {
112
+ ?Score math:notEqualTo 0 .
113
+ ( ?Samples ?Current ?Target ?MutProb ?S0 )
114
+ string:mutateSelectBest
115
+ ( ?Best ?BestScore ?S1 ) .
116
+ ( ?Gen 1 ) :inc ?Gen1 .
117
+ ( ?TraceEvery ?Gen1 ?BestScore ?Best ) :traceMaybe true .
118
+ ( ?Best ?BestScore ?Target ?MutProb ?Samples ?Gen1 ?S1 0 ?TraceEvery )
119
+ :evolve ( ?Final ?FinalGen ?S2 )
120
+ }.
121
+
122
+ # Bounded run (MaxGen > 0): keep evolving while Gen < MaxGen
123
+ { ( ?Current ?Score ?Target ?MutProb ?Samples ?Gen ?S0 ?MaxGen ?TraceEvery )
124
+ :evolve ( ?Final ?FinalGen ?S2 ) } <= {
125
+ ?Score math:notEqualTo 0 .
126
+ ?MaxGen math:greaterThan 0 .
127
+ ?Gen math:lessThan ?MaxGen .
128
+ ( ?Samples ?Current ?Target ?MutProb ?S0 )
129
+ string:mutateSelectBest
130
+ ( ?Best ?BestScore ?S1 ) .
131
+ ( ?Gen 1 ) :inc ?Gen1 .
132
+ ( ?TraceEvery ?Gen1 ?BestScore ?Best ) :traceMaybe true .
133
+ ( ?Best ?BestScore ?Target ?MutProb ?Samples ?Gen1 ?S1 ?MaxGen ?TraceEvery )
134
+ :evolve ( ?Final ?FinalGen ?S2 )
135
+ }.
136
+
137
+ # ----------------------------------
138
+ # Public solver wrapper (string API)
139
+ # ----------------------------------
140
+
141
+ { ?TargetString :solveResult ( ?Gen 0 ?Value ?Seed ) } <= {
38
142
  :cfg :mutationProbability ?MutProb;
39
143
  :samples ?Samples;
40
144
  :seed ?Seed;
41
145
  :traceEvery ?TraceEvery;
42
- :maxGenerations ?MaxG.
146
+ :maxGenerations ?MaxGen.
147
+
148
+ ?TargetString string:length ?Len .
149
+
150
+ ( ?Len ?Seed ) :randomText ( ?Current0 ?S1 ) .
151
+
152
+ # initial score using string:hammingDistance
153
+ ( ?Current0 ?TargetString ) string:hammingDistance ?Score0 .
43
154
 
44
- ( ?TargetString ?MutProb ?Samples ?Seed ?TraceEvery ?MaxG )
45
- ega:solveString
46
- ( ?Gen ?Score ?Value ?SeedOut ) .
155
+ ( ?Current0 ?Score0 ?TargetString ?MutProb ?Samples 0 ?S1 ?MaxGen ?TraceEvery )
156
+ :evolve
157
+ ( ?Value ?Gen ?S2 ) .
47
158
 
48
- # By design, the 4th output is the (input) seed.
49
- ?SeedOut log:equalTo ?Seed
159
+ # Succeeds only when solved:
160
+ ( ?Value ?TargetString ) string:hammingDistance 0 .
161
+ ?Value log:equalTo ?TargetString .
162
+ ?S2 log:equalTo ?_FinalSeedState
50
163
  }.
51
164
 
52
- # Boolean-ish API for querying success
53
165
  { ?TargetString :solved true } <= {
54
166
  ?TargetString :solveResult ( ?Gen 0 ?TargetString ?Seed )
55
167
  }.
@@ -60,4 +172,6 @@
60
172
 
61
173
  { "METHINKS IT IS LIKE A WEASEL" :solved true .
62
174
  ( "solve('%s').\n" "METHINKS IT IS LIKE A WEASEL" ) string:format ?Line
63
- } => { 1 log:outputString ?Line }.
175
+ }
176
+ log:query
177
+ { 1 log:outputString ?Line }.
@@ -11,5 +11,6 @@
11
11
  (3 1000) :ackermann 85720688574901385675874003924800144844912384936442688595500031069628084089994889799455870305255668650207573833404251746014971622855385123487876620597588598431476542198593847883368596840498969135023633457224371799868655530139190140473324351568616503316569571821492337341283438653220995094697645344555005 .
12
12
  (4 0) :ackermann 13 .
13
13
  (4 1) :ackermann 65533 .
14
+ (4 2) :ackermann 2003529930406846464979072351560255750447825475569751419265016973710894059556311453089506130880933348101038234342907263181822949382118812668869506364761547029165041871916351587966347219442930927982084309104855990570159318959639524863372367203002916969592156108764948889254090805911457037675208500206671563702366126359747144807111774815880914135742720967190151836282560618091458852699826141425030123391108273603843767876449043205960379124490905707560314035076162562476031863793126484703743782954975613770981604614413308692118102485959152380195331030292162800160568670105651646750568038741529463842244845292537361442533614373729088303794601274724958414864915930647252015155693922628180691650796381064132275307267143998158508811292628901134237782705567421080070065283963322155077831214288551675554073345107213112427399562982719769150054883905223804357045848197956393157853510018992000024141963706813559840464039472194016069517690156119726982337890017641517190051133466306898140219383481435426387306539552969691388024158161859561100640362119796101859534802787167200122604642492385111393400464351623867567078745259464670903886547743483217897012764455529409092021959585751622973333576159552394885297579954028471943529913543763705986928913757153740001986394332464890052543106629669165243419174691389632476560289415199775477703138064781342309596190960654591300890188887588084733625956065444888501447335706058817090162108499714529568344061979690565469813631162053579369791403236328496233046421066136200220175787851857409162050489711781820400187282939943446186224328009837323764931814789848119452713007440220765680910376203999203492023906626264491909167985461515778839060397720759279378852241294301017458086862263369284725851403039615558564330385450688652213114813638408384778263790459607186876728509763471271988890680478243230394718650525660978150729861141430305816927924971409161059417185352275887504477592218301158780701975535722241400019548102005661773589781499532325208589753463547007786690406429016763808161740550405117670093673202804549339027992491867306539931640720492238474815280619166900933805732120816350707634351669869625020969023162859350071874190579161241536897514808261904847946571736601005892476655445840838334790544144817684255327207315586349347605137419779525190365032198020108764738368682531025183377533908861426184800374008082238104076468878471647552945326947661700424461063311238021134588694532200116564076327023074292426051582811070387018345324567635625951430032037432740780879056283663406965030844225855967039271869461158513793386475699748568670079823960604393478850861649260304945061743412365828352144806726676841807083754862211408236579802961200027441324438432402331257403545019352428776430880232850855886089962774458164680857875115807014743763867976955049991643998284357290415378143438847303484261903388841494031366139854257635577105335580206622185577060082551288893332226436281984838613239570676191409638533832374343758830859233722284644287996245605476932428998432652677378373173288063210753211238680604674708428051166488709084770291208161104912555598322366244868556651402684641209694982590565519216188104341226838996283071654868525536914850299539675503954938371853405900096187489473992880432496373165753803673586710175783994818471798498246948060532081996066183434012476096639519778021441199752546704080608499344178256285092726523709898651539462193004607364507926212975917698293892367015170992091531567814439791248475706237804600009918293321306880570046591458387208088016887445835557926258465124763087148566313528934166117490617526671492672176128330845273936469244582892571388877839056300482483799839692029222215486145902373478222682521639957440801727144146179559226175083889020074169926238300282286249284182671243405751424188569994272331606998712986882771820617214453142574944015066139463169197629181506579745526236191224848063890033669074365989226349564114665503062965960199720636202603521917776740668777463549375318899587866282125469797102065747232721372918144666659421872003474508942830911535189271114287108376159222380276605327823351661555149369375778466670145717971901227117812780450240026384758788339396817962950690798817121690686929538248529830023476068454114178139110648560236549754227497231007615131870024053910510913817843721791422528587432098524957878034683703337818421444017138688124249984418618129271198533315382567321870421530631197748535214670955334626336610864667332292409879849256691109516143618601548909740241913509623043612196128165950518666022030715613684732364660868905014263913906515063908199378852318365059897299125404479443425166774299659811849233151555272883274028352688442408752811283289980625912673699546247341543333500147231430612750390307397135252069338173843322950701049061867539433130784798015655130384758155685236218010419650255596181934986315913233036096461905990236112681196023441843363334594927631946101716652913823717182394299216272538461776065694542297877071383198817036964588689811863210976900355735884624464835706291453052757101278872027965364479724025405448132748391794128826423835171949197209797145936887537198729130831738033911016128547415377377715951728084111627597186384924222802373441925469991983672192131287035585307966942713416391033882754318613643490100943197409047331014476299861725424423355612237435715825933382804986243892498222780715951762757847109475119033482241412025182688713728193104253478196128440176479531505057110722974314569915223451643121848657575786528197564843508958384722923534559464521215831657751471298708225909292655638836651120681943836904116252668710044560243704200663709001941185557160472044643696932850060046928140507119069261393993902735534545567470314903886022024639948260501762431969305640666366626090207048887438898907498152865444381862917382901051820869936382661868303915273264581286782806601337500096593364625146091723180312930347877421234679118454791311109897794648216922505629399956793483801699157439700537542134485874586856047286751065423341893839099110586465595113646061055156838541217459801807133163612573079611168343863767667307354583494789788316330129240800836356825939157113130978030516441716682518346573675934198084958947940983292500086389778563494693212473426103062713745077286156922596628573857905533240641849018451328284632709269753830867308409142247659474439973348130810986399417379789657010687026734161967196591599588537834822988270125605842365589539690306474965584147981310997157542043256395776070485100881578291408250777738559790129129407309462785944505859412273194812753225152324801503466519048228961406646890305102510916237770448486230229488966711380555607956620732449373374027836767300203011615227008921843515652121379215748206859356920790214502277133099987729459596952817044582181956080965811702798062669891205061560742325686842271306295009864421853470810407128917646906550836129916694778023822502789667843489199409657361704586786242554006942516693979292624714524945408858422726153755260071904336329196375777502176005195800693847635789586878489536872122898557806826518192703632099480155874455575175312736471421295536494084385586615208012115079075068553344489258693283859653013272046970694571546959353658571788894862333292465202735853188533370948455403336565356988172582528918056635488363743793348411845580168331827676834646291995605513470039147876808640322629616641560667508153710646723108461964247537490553744805318226002710216400980584497526023035640038083472053149941172965736785066421400842696497103241919182121213206939769143923368374709228267738708132236680086924703491586840991153098315412063566123187504305467536983230827966457417620806593177265685841681837966106144963432544111706941700222657817358351259821080769101961052229263879745049019254311900620561906577452416191913187533984049343976823310298465893318373015809592522829206820862230332585280119266496314441316442773003237792274712330696417149945532261035475145631290668854345426869788447742981777493710117614651624183616680254815296335308490849943006763654806102940094693750609845588558043970485914449584445079978497045583550685408745163316464118083123079704389849190506587586425810738422420591191941674182490452700288263983057950057341711487031187142834184499153456702915280104485145176055306971441761368582384102787659324662689978418319620312262421177391477208004883578333569204533935953254564897028558589735505751235129536540502842081022785248776603574246366673148680279486052445782673626230852978265057114624846595914210278122788941448163994973881884622768244851622051817076722169863265701654316919742651230041757329904473537672536845792754365412826553581858046840069367718605020070547247548400805530424951854495267247261347318174742180078574693465447136036975884118029408039616746946288540679172138601225419503819704538417268006398820656328792839582708510919958839448297775647152026132871089526163417707151642899487953564854553553148754978134009964854498635824847690590033116961303766127923464323129706628411307427046202032013368350385425360313636763575212604707425311209233402837482949453104727418969287275572027615272268283376741393425652653283068469997597097750005560889932685025049212884068274139881631540456490350775871680074055685724021758685439053228133770707415830756269628316955687424060527726485853050611356384851965918968649596335568216975437621430778665934730450164822432964891270709898076676625671517269062058815549666382573829274182082278960684488222983394816670984039024283514306813767253460126007269262969468672750794346190439996618979611928750519442356402644303271737341591281496056168353988188569484045342311424613559925272330064881627466723523751234311893442118885085079358163848994487544756331689213869675574302737953785262542329024881047181939037220666894702204258836895840939998453560948869946833852579675161882159410981624918741813364726965123980677561947912557957446471427868624053750576104204267149366084980238274680575982591331006919941904651906531171908926077949119217946407355129633864523035673345588033313197080365457184791550432654899559705862888286866606618021882248602144999973122164138170653480175510438406624412822803616648904257377640956326482825258407669045608439490325290526337532316509087681336614242398309530806549661879381949120033919489494065132398816642080088395554942237096734840072642705701165089075196155370186264797456381187856175457113400473810762763014953309735174180655479112660938034311378532532883533352024934365979129341284854970946826329075830193072665337782559314331110963848053940859283988907796210479847919686876539987477095912788727475874439806779824968278272200926449944559380414608770641941810440758269805688038949654616587983904660587645341810289907194293021774519976104495043196841503455514044820928933378657363052830619990077748726922998608279053171691876578860908941817057993404890218441559791092676862796597583952483926734883634745651687016166240642424241228961118010615682342539392180052483454723779219911228595914191877491793823340010078128326506710281781396029120914720100947878752551263372884222353869490067927664511634758101193875319657242121476038284774774571704578610417385747911301908583877890152334343013005282797038580359815182929600305682612091950943737325454171056383887047528950563961029843641360935641632589408137981511693338619797339821670761004607980096016024823096943043806956620123213650140549586250615282588033022908385812478469315720323233601899469437647726721879376826431828382603564520699468630216048874528424363593558622333506235945002890558581611275341783750455936126130852640828051213873177490200249552738734585956405160830583053770732533971552620444705429573538361113677523169972740292941674204423248113875075631319078272188864053374694213842169928862940479635305150560788126366206497231257579019598873041195626227343728900516561111094111745277965482790471250581999077498063821559376885546498822938985408291325129076478386322494781016753491693489288104203015610283386143827378160946341335383578340765314321417150655877547820252454780657301342277470616744241968952613164274104695474621483756288299771804186785084546965619150908695874251184435837306590951460980451247409411373899927822492983367796011015387096129749705566301637307202750734759922943792393824427421186158236161317886392553095117188421298508307238259729144142251579403883011359083331651858234967221259621812507058113759495525022747274674369887131926670769299199084467161228738858457584622726573330753735572823951616964175198675012681745429323738294143824814377139861906716657572945807804820559511881687188075212971832636442155336787751274766940790117057509819575084563565217389544179875074523854455200133572033332379895074393905312918212255259833790909463630202185353848854825062897715616963860712382771725621313460549401770413581731931763370136332252819127547191443450920711848838366818174263342949611870091503049165339464763717766439120798347494627397822171502090670190302469762151278521956142070806461631373236517853976292092025500288962012970141379640038055734949269073535145961208674796547733692958773628635660143767964038430796864138563447801328261284589184898528048048844180821639423974014362903481665458114454366460032490618763039502356402044530748210241366895196644221339200757479128683805175150634662569391937740283512075666260829890491877287833852178522792045771846965855278790447562192663992008409302075673925363735628390829817577902153202106409617373283598494066652141198183810884515459772895164572131897797907491941013148368544639616904607030107596818933741217575988165127000761262789169510406315857637534787420070222051070891257612361658026806815858499852631465878086616800733264676830206391697203064894405628195406190685242003053463156621891327309069687353181641094514288036605995220248248886711554429104721929134248346438705368508648749099178812670565665387191049721820042371492740164460943459845392536706132210616533085662021188968234005752675486101476993688738209584552211571923479686888160853631615862880150395949418529489227074410828207169303387818084936204018255222271010985653444817207470756019245915599431072949578197878590578940052540122867517142511184356437184053563024181225473266093302710397968091064939272722683035410467632591355279683837705019855234621222858410557119921731717969804339317707750755627056047831779844447637560254637033369247114220815519973691371975163241302748712199863404548248524570118553342675264715978310731245663429805221455494156252724028915333354349341217862037007260315279870771872491234494477147909520734761385425485311552773301030342476835865496093722324007154518129732692081058424090557725645803681462234493189708138897143299831347617799679712453782310703739151473878692119187566700319321281896803322696594459286210607438827416919465162267632540665070881071030394178860564893769816734159025925194611823642945652669372203155504700213598846292758012527715422016629954863130324912311029627923723899766416803497141226527931907636326136814145516376656559839788489381733082668779901962886932296597379951931621187215455287394170243669885593888793316744533363119541518404088283815193421234122820030950313341050704760159987985472529190665222479319715440331794836837373220821885773341623856441380700541913530245943913502554531886454796252260251762928374330465102361057583514550739443339610216229675461415781127197001738611494279501411253280621254775810512972088465263158094806633687670147310733540717710876615935856814098212967730759197382973441445256688770855324570888958320993823432102718224114763732791357568615421252849657903335093152776925505845644010552192644505312073756287744998163646332835816140330175813967359427327690448920361880386754955751806890058532927201493923500525845146706982628548257883267398735220457228239290207144822219885587102896991935873074277815159757620764023951243860202032596596250212578349957710085626386118233813318509014686577064010676278617583772772895892746039403930337271873850536912957126715066896688493880885142943609962012966759079225082275313812849851526902931700263136328942095797577959327635531162066753488651317323872438748063513314512644889967589828812925480076425186586490241111127301357197181381602583178506932244007998656635371544088454866393181708395735780799059730839094881804060935959190907473960904410150516321749681412100765719177483767355751000733616922386537429079457803200042337452807566153042929014495780629634138383551783599764708851349004856973697965238695845994595592090709058956891451141412684505462117945026611750166928260250950770778211950432617383223562437601776799362796099368975191394965033358507155418436456852616674243688920371037495328425927131610537834980740739158633817967658425258036737206469351248652238481341663808061505704829059890696451936440018597120425723007316410009916987524260377362177763430621616744884930810929901009517974541564251204822086714586849255132444266777127863728211331536224301091824391243380214046242223349153559516890816288487989988273630445372432174280215755777967021666317047969728172483392841015642274507271779269399929740308072770395013581545142494049026536105825409373114653104943382484379718606937214444600826798002471229489405761853892203425608302697052876621377373594394224114707074072902725461307358541745691419446487624357682397065703184168467540733466346293673983620004041400714054277632480132742202685393698869787607009590048684650626771363070979821006557285101306601010780633743344773073478653881742681230743766066643312775356466578603715192922768440458273283243808212841218776132042460464900801054731426749260826922155637405486241717031027919996942645620955619816454547662045022411449404749349832206807191352767986747813458203859570413466177937228534940031631599544093684089572533438702986717829770373332806801764639502090023941931499115009105276821119510999063166150311585582835582607179410052528583611369961303442790173811787412061288182062023263849861515656451230047792967563618345768105043341769543067538041113928553792529241347339481050532025708728186307291158911335942014761872664291564036371927602306283840650425441742335464549987055318726887926424102147363698625463747159744354943443899730051742525110877357886390946812096673428152585919924857640488055071329814299359911463239919113959926752576359007446572810191805841807342227734721397723218231771716916400108826112549093361186780575722391018186168549108500885272274374212086524852372456248697662245384819298671129452945515497030585919307198497105414181636968976131126744027009648667545934567059936995464500558921628047976365686133316563907395703272034389175415267500915011198856872708848195531676931681272892143031376818016445477367518353497857924276463354162433601125960252109501612264110346083465648235597934274056868849224458745493776752120324703803035491157544831295275891939893680876327685438769557694881422844311998595700727521393176837831770339130423060958999137314684569010422095161967070506420256733873446115655276175992727151877660010238944760539789516945708802728736225121076224091810066700883474737605156285533943565843756271241244457651663064085939507947550920463932245202535463634444791755661725962187199279186575490857852950012840229035061514937310107009446151011613712423761426722541732055959202782129325725947146417224977321316381845326555279604270541871496236585252458648933254145062642337885651464670604298564781968461593663288954299780722542264790400616019751975007460545150060291806638271497016110987951336633771378434416194053121445291855180136575558667615019373029691932076120009255065081583275508499340768797252369987023567931026804136745718956641431852679054717169962990363015545645090044802789055701968328313630718997699153166679208958768572290600915472919636381673596673959975710326015571920237348580521128117458610065152598883843114511894880552129145775699146577530041384717124577965048175856395072895337539755822087777506072339445587895905719156733 .
14
15
  (5 0) :ackermann 65533 .
15
16
  } .
@@ -99,7 +99,10 @@ math:rounded a ex:Builtin ; ex:kind ex:Function ;
99
99
  rdfs:comment "Rounds subject to nearest integer (JS tie-breaking: toward +∞). Binds/unifies object with the rounded integer value." .
100
100
 
101
101
  math:exponentiation a ex:Builtin ; ex:kind ex:Function ;
102
- rdfs:comment "Exponentiation. Forward: (base exponent) -> result. Limited inverse: if base is numeric and exponent is a variable, may solve exponent via logs for positive base != 1 and positive result." .
102
+ rdfs:comment "Exponentiation. Forward: (base exponent) -> result. If both arguments are integer literals and exponent is non-negative, Eyeling uses exact BigInt exponentiation (with a safety cap on result size). Otherwise it falls back to Number exponentiation. Limited inverse: if base is numeric and exponent is a variable, may solve exponent via logs for positive base != 1 and positive result (Number mode only)." .
103
+
104
+ math:bigExponentiation a ex:Builtin ; ex:kind ex:Function ;
105
+ rdfs:comment "Exact integer exponentiation (BigInt-only). Shape: (base exponent) math:bigExponentiation result. Requires integer base and non-negative integer exponent; fails if the estimated result size exceeds Eyeling's safety cap." .
103
106
 
104
107
  math:absoluteValue a ex:Builtin ; ex:kind ex:Function ;
105
108
  rdfs:comment "Absolute value. Computes |s| and unifies/binds object; output datatype follows common numeric datatype selection." .
@@ -346,7 +349,5 @@ string:setCharAt a ex:Builtin ; ex:kind ex:Function ;
346
349
  string:hammingDistance a ex:Builtin ; ex:kind ex:Function ;
347
350
  rdfs:comment "Function: subject is a 2-item list (a b). Binds/unifies object with the number of differing positions (Hamming distance). Fails if lengths differ." .
348
351
 
349
- # --- eyeling fast path builtins ------------------------------
350
-
351
- ega:solveString a ex:Builtin ; ex:kind ex:Function ;
352
- rdfs:comment "Eyeling extension: GA solver on strings. Subject list: (target mutationProbability samples seed traceEvery maxGenerations). Object list: (generation score value seed). traceEvery=0 disables tracing; maxGenerations=0 means unlimited." .
352
+ string:mutateSelectBest a ex:Builtin ; ex:kind ex:Function ;
353
+ rdfs:comment "Shape: (samples current target mutProb seedState) string:mutateSelectBest (best bestScore seedState2). Generates samples mutated variants of current (mutating each character with probability mutProb percent), scores each candidate against target (Hamming distance), and returns the best candidate (lowest score). RNG is a deterministic 31-bit LCG, threaded via seedState → seedState2, so runs are reproducible." .
package/eyeling.js CHANGED
@@ -64,6 +64,11 @@ const __parseNumericInfoCache = new Map(); // lit string -> info|null
64
64
  // Caching them retains giant strings/BigInts in global Maps and can cause OOM.
65
65
  const MAX_NUMERIC_CACHE_KEY_LEN = 1024;
66
66
 
67
+ // Safety cap for BigInt exponentiation results.
68
+ // Prevents accidental creation of enormous integers that would OOM the process.
69
+ // (Ackermann(4,2) is ~65k bits, so well below this.)
70
+ const MAX_BIGINT_POW_RESULT_BITS = 2_000_000n;
71
+
67
72
  function __useNumericCacheKey(key) {
68
73
  return typeof key === 'string' && key.length <= MAX_NUMERIC_CACHE_KEY_LEN;
69
74
  }
@@ -603,6 +608,31 @@ function pow10n(k) {
603
608
  return 10n ** BigInt(k);
604
609
  }
605
610
 
611
+ function absBigInt(x) {
612
+ return x < 0n ? -x : x;
613
+ }
614
+
615
+ function bigintBitLength(x) {
616
+ // Returns the bit length of |x| as a BigInt (0 for 0).
617
+ const a = absBigInt(x);
618
+ if (a === 0n) return 0n;
619
+ // toString(2) is fine here: we only use this as a conservative size guard.
620
+ return BigInt(a.toString(2).length);
621
+ }
622
+
623
+ function estimatePowResultBits(base, exp) {
624
+ // Conservative estimate of bit length for |base| ** exp (exp >= 0).
625
+ // For |base| in {0,1} this is tiny; otherwise ~ exp * log2(|base|).
626
+ if (exp === 0n) return 1n;
627
+ const a = absBigInt(base);
628
+ if (a === 0n) return 1n; // 0**k (k>0) => 0
629
+ if (a === 1n) return 1n;
630
+ const bl = bigintBitLength(a);
631
+ // If bl is the bit length, then 2^(bl-1) <= a < 2^bl.
632
+ // So a^exp has < bl*exp bits and >= (bl-1)*exp+1 bits.
633
+ return (bl - 1n) * exp + 1n;
634
+ }
635
+
606
636
  // ===========================================================================
607
637
  // Time & duration builtin helpers
608
638
  // ===========================================================================
@@ -1762,50 +1792,87 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
1762
1792
  }
1763
1793
 
1764
1794
  // math:exponentiation
1765
- if (pv === MATH_NS + 'exponentiation') {
1766
- if (g.s instanceof ListTerm && g.s.elems.length === 2) {
1767
- const baseTerm = g.s.elems[0];
1768
- const expTerm = g.s.elems[1];
1795
+ // math:bigExponentiation (exact-integer BigInt-only variant)
1796
+ if (pv === MATH_NS + 'exponentiation' || pv === MATH_NS + 'bigExponentiation') {
1797
+ const onlyBigInt = pv === MATH_NS + 'bigExponentiation';
1798
+
1799
+ if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
1800
+ const baseTerm = g.s.elems[0];
1801
+ const expTerm = g.s.elems[1];
1802
+
1803
+ // 1) Exact integer mode (BigInt): (base exponent) -> result
1804
+ // This avoids huge intermediate derivations for things like Ackermann(4,2).
1805
+ const baseI = parseIntLiteral(baseTerm);
1806
+ const expI = parseIntLiteral(expTerm);
1807
+ if (baseI !== null && expI !== null && expI >= 0n) {
1808
+ // Size guard: refuse powers that would almost certainly OOM.
1809
+ const estBits = estimatePowResultBits(baseI, expI);
1810
+ if (estBits > MAX_BIGINT_POW_RESULT_BITS) return [];
1811
+
1812
+ let out;
1813
+ try {
1814
+ out = baseI ** expI;
1815
+ } catch {
1816
+ return [];
1817
+ }
1818
+
1819
+ const lit = makeNumericOutputLiteral(out, XSD_INTEGER_DT);
1820
+
1821
+ if (g.o instanceof Var) {
1822
+ const s2 = { ...subst };
1823
+ s2[g.o.name] = lit;
1824
+ return [s2];
1825
+ }
1826
+ if (g.o instanceof Blank) return [{ ...subst }];
1769
1827
 
1770
- const a = parseNum(baseTerm);
1771
- let b = null;
1772
- if (a !== null) b = parseNum(expTerm);
1828
+ const oi = parseIntLiteral(g.o);
1829
+ if (oi !== null && oi === out) return [{ ...subst }];
1773
1830
 
1774
- // Forward mode: base and exponent are numeric
1775
- if (a !== null && b !== null) {
1776
- const cVal = a ** b;
1777
- if (!Number.isFinite(cVal)) return [];
1831
+ const s2 = unifyTerm(g.o, lit, subst);
1832
+ return s2 !== null ? [s2] : [];
1833
+ }
1778
1834
 
1779
- let dtOut = commonNumericDatatype([baseTerm, expTerm], g.o);
1780
- if (dtOut === XSD_INTEGER_DT && !Number.isInteger(cVal)) dtOut = XSD_DECIMAL_DT;
1781
- const lit = makeNumericOutputLiteral(cVal, dtOut);
1835
+ // bigExponentiation is intentionally strict.
1836
+ if (onlyBigInt) return [];
1782
1837
 
1783
- if (g.o instanceof Var) {
1784
- const s2 = { ...subst };
1785
- s2[g.o.name] = lit;
1786
- return [s2];
1787
- }
1788
- if (g.o instanceof Blank) return [{ ...subst }];
1789
- if (numEqualTerm(g.o, cVal)) return [{ ...subst }];
1838
+ // 2) Numeric mode (Number): forward + limited inverse
1839
+ const a = parseNum(baseTerm);
1840
+ const b = a !== null ? parseNum(expTerm) : null;
1841
+
1842
+ // Forward
1843
+ if (a !== null && b !== null) {
1844
+ const cVal = a ** b;
1845
+ if (!Number.isFinite(cVal)) return [];
1846
+
1847
+ let dtOut = commonNumericDatatype([baseTerm, expTerm], g.o);
1848
+ if (dtOut === XSD_INTEGER_DT && !Number.isInteger(cVal)) dtOut = XSD_DECIMAL_DT;
1849
+ const lit = makeNumericOutputLiteral(cVal, dtOut);
1850
+
1851
+ if (g.o instanceof Var) {
1852
+ const s2 = { ...subst };
1853
+ s2[g.o.name] = lit;
1854
+ return [s2];
1790
1855
  }
1856
+ if (g.o instanceof Blank) return [{ ...subst }];
1857
+ if (numEqualTerm(g.o, cVal)) return [{ ...subst }];
1858
+ }
1791
1859
 
1792
- // Inverse mode: solve exponent
1793
- const c = parseNum(g.o);
1794
- if (a !== null && expTerm instanceof Var && c !== null) {
1795
- if (a > 0.0 && a !== 1.0 && c > 0.0) {
1796
- const bVal = Math.log(c) / Math.log(a);
1797
- if (!Number.isFinite(bVal)) return [];
1860
+ // Inverse: solve exponent using logs (Number mode only)
1861
+ const c = parseNum(g.o);
1862
+ if (a !== null && expTerm instanceof Var && c !== null) {
1863
+ if (a > 0.0 && a !== 1.0 && c > 0.0) {
1864
+ const bVal = Math.log(c) / Math.log(a);
1865
+ if (!Number.isFinite(bVal)) return [];
1798
1866
 
1799
- let dtB = commonNumericDatatype([baseTerm, g.o], expTerm);
1800
- if (dtB === XSD_INTEGER_DT && !Number.isInteger(bVal)) dtB = XSD_DECIMAL_DT;
1867
+ let dtB = commonNumericDatatype([baseTerm, g.o], expTerm);
1868
+ if (dtB === XSD_INTEGER_DT && !Number.isInteger(bVal)) dtB = XSD_DECIMAL_DT;
1801
1869
 
1802
- const s2 = { ...subst };
1803
- s2[expTerm.name] = makeNumericOutputLiteral(bVal, dtB);
1804
- return [s2];
1805
- }
1870
+ const s2 = { ...subst };
1871
+ s2[expTerm.name] = makeNumericOutputLiteral(bVal, dtB);
1872
+ return [s2];
1806
1873
  }
1807
- return [];
1808
1874
  }
1875
+ return [];
1809
1876
  }
1810
1877
 
1811
1878
  // math:absoluteValue
@@ -3547,34 +3614,32 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3547
3614
  return s2 !== null ? [s2] : [];
3548
3615
  }
3549
3616
 
3550
- // -----------------------------------------------------------------
3551
- // 4.6.2 urn:eyeling:ga: builtins (fast GA on *strings*)
3552
- // -----------------------------------------------------------------
3553
-
3554
- // urn:eyeling:ga:solveString
3555
- // Schema: ( $target $mutProb $samples $seed $traceEvery $maxGenerations ) :solveString ( $gen $score $value $seed )
3556
- // Side effect: if traceEvery > 0, prints: <gen> TRACE (<score> "<value>")
3557
- if (pv === 'urn:eyeling:ga:solveString') {
3558
- if (!(g.s instanceof ListTerm) || g.s.elems.length != 6) return [];
3559
-
3560
- const target = termToJsXsdStringNoLang(g.s.elems[0]);
3561
- const mutProbNum = parseNum(g.s.elems[1]);
3562
- const samplesNum = parseNum(g.s.elems[2]);
3563
- const seedNum = parseNum(g.s.elems[3]);
3564
- const traceEveryNum = parseNum(g.s.elems[4]);
3565
- const maxGenNum = parseNum(g.s.elems[5]);
3566
-
3567
- if (target === null || mutProbNum === null || samplesNum === null || seedNum === null) return [];
3568
- if (traceEveryNum === null || maxGenNum === null) return [];
3617
+ // string:mutateSelectBest
3618
+ // Schema:
3619
+ // ( $samples $current $target $mutProb $seedState )
3620
+ // string:mutateSelectBest
3621
+ // ( $best $bestScore $seedState2 )
3622
+ //
3623
+ // Deterministic 31-bit LCG (same as the GA demo):
3624
+ // state = (1103515245 * state + 12345) % 2^31
3625
+ // and randRound(N) = Math.round((state/2^31) * N) with state advanced once per draw.
3626
+ if (pv === STRING_NS + 'mutateSelectBest') {
3627
+ if (!(g.s instanceof ListTerm) || g.s.elems.length !== 5) return [];
3628
+
3629
+ const samplesNum = parseNum(g.s.elems[0]);
3630
+ const current = termToJsXsdStringNoLang(g.s.elems[1]);
3631
+ const target = termToJsXsdStringNoLang(g.s.elems[2]);
3632
+ const mutProbNum = parseNum(g.s.elems[3]);
3633
+ const seedNum = parseNum(g.s.elems[4]);
3634
+
3635
+ if (samplesNum === null || current === null || target === null || mutProbNum === null || seedNum === null)
3636
+ return [];
3637
+ if (current.length !== target.length) return [];
3569
3638
 
3570
- const mutProb = Math.max(0, Math.trunc(mutProbNum));
3571
3639
  const samples = Math.max(1, Math.trunc(samplesNum));
3640
+ const mutProb = Math.max(0, Math.trunc(mutProbNum));
3572
3641
  const seed0 = Math.trunc(seedNum);
3573
3642
 
3574
- const traceEvery = Math.max(0, Math.trunc(traceEveryNum));
3575
- const maxGenerations = Math.max(0, Math.trunc(maxGenNum));
3576
-
3577
- // Deterministic 31-bit LCG using BigInt arithmetic (matches the N3 version)
3578
3643
  let state = BigInt(seed0);
3579
3644
  const MOD = 2147483648n; // 2^31
3580
3645
  const A = 1103515245n;
@@ -3594,12 +3659,6 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3594
3659
  return p === 0 ? ' ' : String.fromCharCode(64 + p);
3595
3660
  }
3596
3661
 
3597
- function randomText(len) {
3598
- let out = '';
3599
- for (let i = 0; i < len; i++) out += randomAlpha();
3600
- return out;
3601
- }
3602
-
3603
3662
  function mutate(str) {
3604
3663
  let out = '';
3605
3664
  for (let i = 0; i < str.length; i++) {
@@ -3616,50 +3675,22 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3616
3675
  return diffs;
3617
3676
  }
3618
3677
 
3619
- const pref = trace.getTracePrefixes() || PrefixEnv.newDefault();
3620
- function doTrace(gen, sc, val) {
3621
- const genLit = internLiteral(String(gen));
3622
- const scLit = internLiteral(String(sc));
3623
- const valLit = makeStringLiteral(val);
3624
- const obj = new ListTerm([scLit, valLit]);
3625
- const xStr = termToN3(genLit, pref);
3626
- const yStr = termToN3(obj, pref);
3627
- trace.writeTraceLine(`${xStr} TRACE ${yStr}`);
3628
- }
3629
-
3630
- let generation = 0;
3631
- let current = randomText(target.length);
3632
- let currentScore = score(current);
3633
-
3634
- if (traceEvery > 0 && generation % traceEvery === 0) doTrace(generation, currentScore, current);
3635
-
3636
- while (currentScore !== 0) {
3637
- if (maxGenerations > 0 && generation >= maxGenerations) break;
3638
-
3639
- let best = '';
3640
- let bestScore = Infinity;
3641
-
3642
- for (let i = 0; i < samples; i++) {
3643
- const cand = mutate(current);
3644
- const candScore = score(cand);
3645
- if (candScore < bestScore) {
3646
- best = cand;
3647
- bestScore = candScore;
3648
- }
3649
- }
3650
-
3651
- generation += 1;
3652
- current = best;
3653
- currentScore = bestScore;
3678
+ let best = '';
3679
+ let bestScore = Infinity;
3654
3680
 
3655
- if (traceEvery > 0 && generation % traceEvery === 0) doTrace(generation, currentScore, current);
3681
+ for (let i = 0; i < samples; i++) {
3682
+ const cand = mutate(current);
3683
+ const candScore = score(cand);
3684
+ if (candScore < bestScore) {
3685
+ best = cand;
3686
+ bestScore = candScore;
3687
+ }
3656
3688
  }
3657
3689
 
3658
3690
  const outList = new ListTerm([
3659
- internLiteral(String(generation)),
3660
- internLiteral(String(currentScore)),
3661
- makeStringLiteral(current),
3662
- internLiteral(String(seed0)),
3691
+ makeStringLiteral(best),
3692
+ internLiteral(String(bestScore)),
3693
+ internLiteral(state.toString()),
3663
3694
  ]);
3664
3695
 
3665
3696
  const s2 = unifyTerm(g.o, outList, subst);
@@ -3686,11 +3717,6 @@ function isBuiltinPred(p) {
3686
3717
  return true;
3687
3718
  }
3688
3719
 
3689
- // Eyeling extension: GA demo builtins
3690
- if (v === 'urn:eyeling:ga:solveString') {
3691
- return true;
3692
- }
3693
-
3694
3720
  return (
3695
3721
  v.startsWith(CRYPTO_NS) ||
3696
3722
  v.startsWith(MATH_NS) ||
package/lib/builtins.js CHANGED
@@ -52,6 +52,11 @@ const __parseNumericInfoCache = new Map(); // lit string -> info|null
52
52
  // Caching them retains giant strings/BigInts in global Maps and can cause OOM.
53
53
  const MAX_NUMERIC_CACHE_KEY_LEN = 1024;
54
54
 
55
+ // Safety cap for BigInt exponentiation results.
56
+ // Prevents accidental creation of enormous integers that would OOM the process.
57
+ // (Ackermann(4,2) is ~65k bits, so well below this.)
58
+ const MAX_BIGINT_POW_RESULT_BITS = 2_000_000n;
59
+
55
60
  function __useNumericCacheKey(key) {
56
61
  return typeof key === 'string' && key.length <= MAX_NUMERIC_CACHE_KEY_LEN;
57
62
  }
@@ -591,6 +596,31 @@ function pow10n(k) {
591
596
  return 10n ** BigInt(k);
592
597
  }
593
598
 
599
+ function absBigInt(x) {
600
+ return x < 0n ? -x : x;
601
+ }
602
+
603
+ function bigintBitLength(x) {
604
+ // Returns the bit length of |x| as a BigInt (0 for 0).
605
+ const a = absBigInt(x);
606
+ if (a === 0n) return 0n;
607
+ // toString(2) is fine here: we only use this as a conservative size guard.
608
+ return BigInt(a.toString(2).length);
609
+ }
610
+
611
+ function estimatePowResultBits(base, exp) {
612
+ // Conservative estimate of bit length for |base| ** exp (exp >= 0).
613
+ // For |base| in {0,1} this is tiny; otherwise ~ exp * log2(|base|).
614
+ if (exp === 0n) return 1n;
615
+ const a = absBigInt(base);
616
+ if (a === 0n) return 1n; // 0**k (k>0) => 0
617
+ if (a === 1n) return 1n;
618
+ const bl = bigintBitLength(a);
619
+ // If bl is the bit length, then 2^(bl-1) <= a < 2^bl.
620
+ // So a^exp has < bl*exp bits and >= (bl-1)*exp+1 bits.
621
+ return (bl - 1n) * exp + 1n;
622
+ }
623
+
594
624
  // ===========================================================================
595
625
  // Time & duration builtin helpers
596
626
  // ===========================================================================
@@ -1750,50 +1780,87 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
1750
1780
  }
1751
1781
 
1752
1782
  // math:exponentiation
1753
- if (pv === MATH_NS + 'exponentiation') {
1754
- if (g.s instanceof ListTerm && g.s.elems.length === 2) {
1755
- const baseTerm = g.s.elems[0];
1756
- const expTerm = g.s.elems[1];
1783
+ // math:bigExponentiation (exact-integer BigInt-only variant)
1784
+ if (pv === MATH_NS + 'exponentiation' || pv === MATH_NS + 'bigExponentiation') {
1785
+ const onlyBigInt = pv === MATH_NS + 'bigExponentiation';
1786
+
1787
+ if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
1788
+ const baseTerm = g.s.elems[0];
1789
+ const expTerm = g.s.elems[1];
1790
+
1791
+ // 1) Exact integer mode (BigInt): (base exponent) -> result
1792
+ // This avoids huge intermediate derivations for things like Ackermann(4,2).
1793
+ const baseI = parseIntLiteral(baseTerm);
1794
+ const expI = parseIntLiteral(expTerm);
1795
+ if (baseI !== null && expI !== null && expI >= 0n) {
1796
+ // Size guard: refuse powers that would almost certainly OOM.
1797
+ const estBits = estimatePowResultBits(baseI, expI);
1798
+ if (estBits > MAX_BIGINT_POW_RESULT_BITS) return [];
1799
+
1800
+ let out;
1801
+ try {
1802
+ out = baseI ** expI;
1803
+ } catch {
1804
+ return [];
1805
+ }
1806
+
1807
+ const lit = makeNumericOutputLiteral(out, XSD_INTEGER_DT);
1808
+
1809
+ if (g.o instanceof Var) {
1810
+ const s2 = { ...subst };
1811
+ s2[g.o.name] = lit;
1812
+ return [s2];
1813
+ }
1814
+ if (g.o instanceof Blank) return [{ ...subst }];
1757
1815
 
1758
- const a = parseNum(baseTerm);
1759
- let b = null;
1760
- if (a !== null) b = parseNum(expTerm);
1816
+ const oi = parseIntLiteral(g.o);
1817
+ if (oi !== null && oi === out) return [{ ...subst }];
1761
1818
 
1762
- // Forward mode: base and exponent are numeric
1763
- if (a !== null && b !== null) {
1764
- const cVal = a ** b;
1765
- if (!Number.isFinite(cVal)) return [];
1819
+ const s2 = unifyTerm(g.o, lit, subst);
1820
+ return s2 !== null ? [s2] : [];
1821
+ }
1766
1822
 
1767
- let dtOut = commonNumericDatatype([baseTerm, expTerm], g.o);
1768
- if (dtOut === XSD_INTEGER_DT && !Number.isInteger(cVal)) dtOut = XSD_DECIMAL_DT;
1769
- const lit = makeNumericOutputLiteral(cVal, dtOut);
1823
+ // bigExponentiation is intentionally strict.
1824
+ if (onlyBigInt) return [];
1770
1825
 
1771
- if (g.o instanceof Var) {
1772
- const s2 = { ...subst };
1773
- s2[g.o.name] = lit;
1774
- return [s2];
1775
- }
1776
- if (g.o instanceof Blank) return [{ ...subst }];
1777
- if (numEqualTerm(g.o, cVal)) return [{ ...subst }];
1826
+ // 2) Numeric mode (Number): forward + limited inverse
1827
+ const a = parseNum(baseTerm);
1828
+ const b = a !== null ? parseNum(expTerm) : null;
1829
+
1830
+ // Forward
1831
+ if (a !== null && b !== null) {
1832
+ const cVal = a ** b;
1833
+ if (!Number.isFinite(cVal)) return [];
1834
+
1835
+ let dtOut = commonNumericDatatype([baseTerm, expTerm], g.o);
1836
+ if (dtOut === XSD_INTEGER_DT && !Number.isInteger(cVal)) dtOut = XSD_DECIMAL_DT;
1837
+ const lit = makeNumericOutputLiteral(cVal, dtOut);
1838
+
1839
+ if (g.o instanceof Var) {
1840
+ const s2 = { ...subst };
1841
+ s2[g.o.name] = lit;
1842
+ return [s2];
1778
1843
  }
1844
+ if (g.o instanceof Blank) return [{ ...subst }];
1845
+ if (numEqualTerm(g.o, cVal)) return [{ ...subst }];
1846
+ }
1779
1847
 
1780
- // Inverse mode: solve exponent
1781
- const c = parseNum(g.o);
1782
- if (a !== null && expTerm instanceof Var && c !== null) {
1783
- if (a > 0.0 && a !== 1.0 && c > 0.0) {
1784
- const bVal = Math.log(c) / Math.log(a);
1785
- if (!Number.isFinite(bVal)) return [];
1848
+ // Inverse: solve exponent using logs (Number mode only)
1849
+ const c = parseNum(g.o);
1850
+ if (a !== null && expTerm instanceof Var && c !== null) {
1851
+ if (a > 0.0 && a !== 1.0 && c > 0.0) {
1852
+ const bVal = Math.log(c) / Math.log(a);
1853
+ if (!Number.isFinite(bVal)) return [];
1786
1854
 
1787
- let dtB = commonNumericDatatype([baseTerm, g.o], expTerm);
1788
- if (dtB === XSD_INTEGER_DT && !Number.isInteger(bVal)) dtB = XSD_DECIMAL_DT;
1855
+ let dtB = commonNumericDatatype([baseTerm, g.o], expTerm);
1856
+ if (dtB === XSD_INTEGER_DT && !Number.isInteger(bVal)) dtB = XSD_DECIMAL_DT;
1789
1857
 
1790
- const s2 = { ...subst };
1791
- s2[expTerm.name] = makeNumericOutputLiteral(bVal, dtB);
1792
- return [s2];
1793
- }
1858
+ const s2 = { ...subst };
1859
+ s2[expTerm.name] = makeNumericOutputLiteral(bVal, dtB);
1860
+ return [s2];
1794
1861
  }
1795
- return [];
1796
1862
  }
1863
+ return [];
1797
1864
  }
1798
1865
 
1799
1866
  // math:absoluteValue
@@ -3535,34 +3602,32 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3535
3602
  return s2 !== null ? [s2] : [];
3536
3603
  }
3537
3604
 
3538
- // -----------------------------------------------------------------
3539
- // 4.6.2 urn:eyeling:ga: builtins (fast GA on *strings*)
3540
- // -----------------------------------------------------------------
3541
-
3542
- // urn:eyeling:ga:solveString
3543
- // Schema: ( $target $mutProb $samples $seed $traceEvery $maxGenerations ) :solveString ( $gen $score $value $seed )
3544
- // Side effect: if traceEvery > 0, prints: <gen> TRACE (<score> "<value>")
3545
- if (pv === 'urn:eyeling:ga:solveString') {
3546
- if (!(g.s instanceof ListTerm) || g.s.elems.length != 6) return [];
3547
-
3548
- const target = termToJsXsdStringNoLang(g.s.elems[0]);
3549
- const mutProbNum = parseNum(g.s.elems[1]);
3550
- const samplesNum = parseNum(g.s.elems[2]);
3551
- const seedNum = parseNum(g.s.elems[3]);
3552
- const traceEveryNum = parseNum(g.s.elems[4]);
3553
- const maxGenNum = parseNum(g.s.elems[5]);
3554
-
3555
- if (target === null || mutProbNum === null || samplesNum === null || seedNum === null) return [];
3556
- if (traceEveryNum === null || maxGenNum === null) return [];
3605
+ // string:mutateSelectBest
3606
+ // Schema:
3607
+ // ( $samples $current $target $mutProb $seedState )
3608
+ // string:mutateSelectBest
3609
+ // ( $best $bestScore $seedState2 )
3610
+ //
3611
+ // Deterministic 31-bit LCG (same as the GA demo):
3612
+ // state = (1103515245 * state + 12345) % 2^31
3613
+ // and randRound(N) = Math.round((state/2^31) * N) with state advanced once per draw.
3614
+ if (pv === STRING_NS + 'mutateSelectBest') {
3615
+ if (!(g.s instanceof ListTerm) || g.s.elems.length !== 5) return [];
3616
+
3617
+ const samplesNum = parseNum(g.s.elems[0]);
3618
+ const current = termToJsXsdStringNoLang(g.s.elems[1]);
3619
+ const target = termToJsXsdStringNoLang(g.s.elems[2]);
3620
+ const mutProbNum = parseNum(g.s.elems[3]);
3621
+ const seedNum = parseNum(g.s.elems[4]);
3622
+
3623
+ if (samplesNum === null || current === null || target === null || mutProbNum === null || seedNum === null)
3624
+ return [];
3625
+ if (current.length !== target.length) return [];
3557
3626
 
3558
- const mutProb = Math.max(0, Math.trunc(mutProbNum));
3559
3627
  const samples = Math.max(1, Math.trunc(samplesNum));
3628
+ const mutProb = Math.max(0, Math.trunc(mutProbNum));
3560
3629
  const seed0 = Math.trunc(seedNum);
3561
3630
 
3562
- const traceEvery = Math.max(0, Math.trunc(traceEveryNum));
3563
- const maxGenerations = Math.max(0, Math.trunc(maxGenNum));
3564
-
3565
- // Deterministic 31-bit LCG using BigInt arithmetic (matches the N3 version)
3566
3631
  let state = BigInt(seed0);
3567
3632
  const MOD = 2147483648n; // 2^31
3568
3633
  const A = 1103515245n;
@@ -3582,12 +3647,6 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3582
3647
  return p === 0 ? ' ' : String.fromCharCode(64 + p);
3583
3648
  }
3584
3649
 
3585
- function randomText(len) {
3586
- let out = '';
3587
- for (let i = 0; i < len; i++) out += randomAlpha();
3588
- return out;
3589
- }
3590
-
3591
3650
  function mutate(str) {
3592
3651
  let out = '';
3593
3652
  for (let i = 0; i < str.length; i++) {
@@ -3604,50 +3663,22 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3604
3663
  return diffs;
3605
3664
  }
3606
3665
 
3607
- const pref = trace.getTracePrefixes() || PrefixEnv.newDefault();
3608
- function doTrace(gen, sc, val) {
3609
- const genLit = internLiteral(String(gen));
3610
- const scLit = internLiteral(String(sc));
3611
- const valLit = makeStringLiteral(val);
3612
- const obj = new ListTerm([scLit, valLit]);
3613
- const xStr = termToN3(genLit, pref);
3614
- const yStr = termToN3(obj, pref);
3615
- trace.writeTraceLine(`${xStr} TRACE ${yStr}`);
3616
- }
3617
-
3618
- let generation = 0;
3619
- let current = randomText(target.length);
3620
- let currentScore = score(current);
3621
-
3622
- if (traceEvery > 0 && generation % traceEvery === 0) doTrace(generation, currentScore, current);
3623
-
3624
- while (currentScore !== 0) {
3625
- if (maxGenerations > 0 && generation >= maxGenerations) break;
3626
-
3627
- let best = '';
3628
- let bestScore = Infinity;
3629
-
3630
- for (let i = 0; i < samples; i++) {
3631
- const cand = mutate(current);
3632
- const candScore = score(cand);
3633
- if (candScore < bestScore) {
3634
- best = cand;
3635
- bestScore = candScore;
3636
- }
3637
- }
3638
-
3639
- generation += 1;
3640
- current = best;
3641
- currentScore = bestScore;
3666
+ let best = '';
3667
+ let bestScore = Infinity;
3642
3668
 
3643
- if (traceEvery > 0 && generation % traceEvery === 0) doTrace(generation, currentScore, current);
3669
+ for (let i = 0; i < samples; i++) {
3670
+ const cand = mutate(current);
3671
+ const candScore = score(cand);
3672
+ if (candScore < bestScore) {
3673
+ best = cand;
3674
+ bestScore = candScore;
3675
+ }
3644
3676
  }
3645
3677
 
3646
3678
  const outList = new ListTerm([
3647
- internLiteral(String(generation)),
3648
- internLiteral(String(currentScore)),
3649
- makeStringLiteral(current),
3650
- internLiteral(String(seed0)),
3679
+ makeStringLiteral(best),
3680
+ internLiteral(String(bestScore)),
3681
+ internLiteral(state.toString()),
3651
3682
  ]);
3652
3683
 
3653
3684
  const s2 = unifyTerm(g.o, outList, subst);
@@ -3674,11 +3705,6 @@ function isBuiltinPred(p) {
3674
3705
  return true;
3675
3706
  }
3676
3707
 
3677
- // Eyeling extension: GA demo builtins
3678
- if (v === 'urn:eyeling:ga:solveString') {
3679
- return true;
3680
- }
3681
-
3682
3708
  return (
3683
3709
  v.startsWith(CRYPTO_NS) ||
3684
3710
  v.startsWith(MATH_NS) ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.14.0",
3
+ "version": "1.14.2",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [