eyeling 1.17.2 → 1.18.1

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.
@@ -0,0 +1,219 @@
1
+ # ============================================================================
2
+ # BMI — ARC-style Body Mass Index example.
3
+ #
4
+ # This example turns a familiar health calculation into a small, inspectable
5
+ # ARC program. It normalizes either metric or US inputs, computes BMI, assigns
6
+ # a WHO adult category, and derives a healthy-range weight band for the given
7
+ # height. The report explains the result and includes independent checks for
8
+ # boundary handling and category behavior.
9
+ #
10
+ # For reproducibility and documentation only; not medical advice.
11
+ # ============================================================================
12
+
13
+ @prefix : <https://example.org/bmi#> .
14
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
15
+ @prefix math: <http://www.w3.org/2000/10/swap/math#> .
16
+ @prefix string: <http://www.w3.org/2000/10/swap/string#> .
17
+
18
+ # --------------
19
+ # Editable input
20
+ # --------------
21
+
22
+ :Input
23
+ :unitSystem "metric";
24
+ :weight 72.0;
25
+ :height 178.0.
26
+
27
+ # US alternative:
28
+ # :Input
29
+ # :unitSystem "us";
30
+ # :weight 158.73;
31
+ # :height 70.08.
32
+
33
+ # ---------------------------------
34
+ # Normalization and BMI calculation
35
+ # ---------------------------------
36
+
37
+ { :Input :unitSystem "metric"; :weight ?W; :height ?H.
38
+ (?H 100.0) math:quotient ?M. }
39
+ =>
40
+ { :Case :weightKg ?W; :heightM ?M.
41
+ :Reason :units "Inputs were already metric, so kilograms stay kilograms and centimeters are divided by 100 to obtain meters.". } .
42
+
43
+ { :Input :unitSystem "us"; :weight ?W; :height ?H.
44
+ (?W 0.45359237) math:product ?Kg.
45
+ (?H 0.0254) math:product ?M. }
46
+ =>
47
+ { :Case :weightKg ?Kg; :heightM ?M.
48
+ :Reason :units "US inputs were converted to SI units: pounds to kilograms and inches to meters.". } .
49
+
50
+ { :Case :weightKg ?Kg; :heightM ?M.
51
+ (?M ?M) math:product ?M2.
52
+ (?Kg ?M2) math:quotient ?Bmi.
53
+ (?Bmi 100.0) math:product ?BmiX100.
54
+ ?BmiX100 math:rounded ?BmiRoundedInt.
55
+ (?BmiRoundedInt 100.0) math:quotient ?BmiRounded.
56
+ (18.5 ?M2) math:product ?HealthyMin.
57
+ (24.9 ?M2) math:product ?HealthyMax.
58
+ (?HealthyMin 10.0) math:product ?MinX10.
59
+ (?HealthyMax 10.0) math:product ?MaxX10.
60
+ ?MinX10 math:rounded ?MinRoundedInt.
61
+ ?MaxX10 math:rounded ?MaxRoundedInt.
62
+ (?MinRoundedInt 10.0) math:quotient ?HealthyMinRounded.
63
+ (?MaxRoundedInt 10.0) math:quotient ?HealthyMaxRounded. }
64
+ =>
65
+ { :Case :heightSquared ?M2;
66
+ :bmi ?Bmi;
67
+ :bmiRounded ?BmiRounded;
68
+ :healthyMinKg ?HealthyMin;
69
+ :healthyMaxKg ?HealthyMax;
70
+ :healthyMinKgRounded ?HealthyMinRounded;
71
+ :healthyMaxKgRounded ?HealthyMaxRounded. } .
72
+
73
+ # ------------------------------------------
74
+ # WHO adult categories (half-open intervals)
75
+ # ------------------------------------------
76
+
77
+ { :Case :bmi ?Bmi. ?Bmi math:lessThan 18.5. }
78
+ => { :Decision :category "Underweight". } .
79
+
80
+ { :Case :bmi ?Bmi. ?Bmi math:notLessThan 18.5. ?Bmi math:lessThan 25.0. }
81
+ => { :Decision :category "Normal". } .
82
+
83
+ { :Case :bmi ?Bmi. ?Bmi math:notLessThan 25.0. ?Bmi math:lessThan 30.0. }
84
+ => { :Decision :category "Overweight". } .
85
+
86
+ { :Case :bmi ?Bmi. ?Bmi math:notLessThan 30.0. ?Bmi math:lessThan 35.0. }
87
+ => { :Decision :category "Obesity I". } .
88
+
89
+ { :Case :bmi ?Bmi. ?Bmi math:notLessThan 35.0. ?Bmi math:lessThan 40.0. }
90
+ => { :Decision :category "Obesity II". } .
91
+
92
+ { :Case :bmi ?Bmi. ?Bmi math:notLessThan 40.0. }
93
+ => { :Decision :category "Obesity III". } .
94
+
95
+ # ---------------------
96
+ # Answer and reason why
97
+ # ---------------------
98
+
99
+ { :Case :bmiRounded ?BmiRounded;
100
+ :healthyMinKgRounded ?HealthyMinRounded;
101
+ :healthyMaxKgRounded ?HealthyMaxRounded;
102
+ :heightM ?M.
103
+ :Decision :category ?Category.
104
+ (?M 100.0) math:product ?Cm.
105
+ ?Cm math:rounded ?CmRounded. }
106
+ =>
107
+ { :Answer :bmi ?BmiRounded;
108
+ :category ?Category;
109
+ :healthyMinKg ?HealthyMinRounded;
110
+ :healthyMaxKg ?HealthyMaxRounded;
111
+ :heightCm ?CmRounded. } .
112
+
113
+ { :Case :weightKg ?Kg; :heightM ?M; :heightSquared ?M2; :bmiRounded ?BmiRounded.
114
+ :Reason :units ?Units.
115
+ :Decision :category ?Category. }
116
+ =>
117
+ { :Reason :formula "BMI is defined as weight in kilograms divided by height in meters squared.";
118
+ :calculation "The normalized weight and height were used to compute BMI, then the result was mapped to the WHO adult category table.";
119
+ :categoryRule ?Category;
120
+ :unitsExplanation ?Units. } .
121
+
122
+ # ------------------
123
+ # Independent checks
124
+ # ------------------
125
+
126
+ { :Case :weightKg ?Kg; :heightM ?M. ?Kg math:greaterThan 0. ?M math:greaterThan 0. }
127
+ => { :Check :c1 "OK - the input was normalized into positive SI values.". } .
128
+
129
+ { :Case :heightM ?M; :heightSquared ?M2. (?M ?M) math:product ?M2. }
130
+ => { :Check :c2 "OK - height squared was reconstructed from the normalized height.". } .
131
+
132
+ { :Case :weightKg ?Kg; :heightSquared ?M2; :bmi ?Bmi. (?Kg ?M2) math:quotient ?Bmi. }
133
+ => { :Check :c3 "OK - the BMI value matches the BMI = kg / m² formula.". } .
134
+
135
+ { 18.49 math:lessThan 18.5. }
136
+ => { :Check :c4 "OK - a BMI of 18.49 stays below the normal-weight threshold.". } .
137
+
138
+ { 18.5 math:notLessThan 18.5. 18.5 math:lessThan 25.0. }
139
+ => { :Check :c5 "OK - the lower boundary is half-open: BMI 18.5 is classified as Normal.". } .
140
+
141
+ { 25.0 math:notLessThan 25.0. 25.0 math:lessThan 30.0. }
142
+ => { :Check :c6 "OK - BMI 25.0 starts the Overweight category.". } .
143
+
144
+ { 30.0 math:notLessThan 30.0. 30.0 math:lessThan 35.0. }
145
+ => { :Check :c7 "OK - BMI 30.0 starts the Obesity I category.". } .
146
+
147
+ { 22.0 math:notLessThan 18.5. 22.0 math:lessThan 25.0.
148
+ 27.0 math:notLessThan 25.0. 27.0 math:lessThan 30.0.
149
+ 41.0 math:notLessThan 40.0. }
150
+ => { :Check :c8 "OK - classification behavior is monotonic across representative BMI values.". } .
151
+
152
+ { :Case :heightSquared ?M2; :healthyMinKg ?Min; :healthyMaxKg ?Max.
153
+ (18.5 ?M2) math:product ?Min.
154
+ (24.9 ?M2) math:product ?Max. }
155
+ => { :Check :c9 "OK - the healthy-weight band was reconstructed from BMI 18.5 to 24.9 at the same height.". } .
156
+
157
+ # -----
158
+ # Fuses
159
+ # -----
160
+
161
+ { :Decision :category ?Any.
162
+ 1 log:notIncludes {
163
+ :Check :c1 ?C1.
164
+ :Check :c2 ?C2.
165
+ :Check :c3 ?C3.
166
+ :Check :c4 ?C4.
167
+ :Check :c5 ?C5.
168
+ :Check :c6 ?C6.
169
+ :Check :c7 ?C7.
170
+ :Check :c8 ?C8.
171
+ :Check :c9 ?C9.
172
+ }. }
173
+ => false .
174
+
175
+ { :Decision :category ?C1, ?C2.
176
+ ?C1 log:notEqualTo ?C2. }
177
+ => false .
178
+
179
+ # ---------------------------------------------
180
+ # ARC report (built from actual derived values)
181
+ # ---------------------------------------------
182
+
183
+ { :Answer :bmi ?BmiRounded;
184
+ :category ?Category;
185
+ :healthyMinKg ?HealthyMinRounded;
186
+ :healthyMaxKg ?HealthyMaxRounded;
187
+ :heightCm ?CmRounded.
188
+ :Check :c1 ?C1.
189
+ :Check :c2 ?C2.
190
+ :Check :c3 ?C3.
191
+ :Check :c4 ?C4.
192
+ :Check :c5 ?C5.
193
+ :Check :c6 ?C6.
194
+ :Check :c7 ?C7.
195
+ :Check :c8 ?C8.
196
+ :Check :c9 ?C9.
197
+ (
198
+ "BMI — ARC-style Body Mass Index example\n\n"
199
+ "Answer\n"
200
+ "BMI = " ?BmiRounded "\n"
201
+ "Category = " ?Category "\n"
202
+ "At height " ?CmRounded " cm, a healthy-weight range is about " ?HealthyMinRounded "–" ?HealthyMaxRounded " kg (BMI 18.5–24.9).\n\n"
203
+ "Reason Why\n"
204
+ "BMI is defined as weight in kilograms divided by height in meters squared. "
205
+ "This program first normalizes the input to SI units, computes BMI, and then applies WHO adult categories as half-open intervals. "
206
+ "The healthy-weight band is the weight range at the same height that corresponds to BMI 18.5 through 24.9.\n\n"
207
+ "Check\n"
208
+ "C1 " ?C1 "\n"
209
+ "C2 " ?C2 "\n"
210
+ "C3 " ?C3 "\n"
211
+ "C4 " ?C4 "\n"
212
+ "C5 " ?C5 "\n"
213
+ "C6 " ?C6 "\n"
214
+ "C7 " ?C7 "\n"
215
+ "C8 " ?C8 "\n"
216
+ "C9 " ?C9 "\n"
217
+ ) string:concatenation ?Block. }
218
+ =>
219
+ { :report log:outputString ?Block. } .
@@ -0,0 +1,20 @@
1
+ BMI — ARC-style Body Mass Index example
2
+
3
+ Answer
4
+ BMI = 22.72
5
+ Category = Normal
6
+ At height 178 cm, a healthy-weight range is about 58.6–78.9 kg (BMI 18.5–24.9).
7
+
8
+ Reason Why
9
+ BMI is defined as weight in kilograms divided by height in meters squared. This program first normalizes the input to SI units, computes BMI, and then applies WHO adult categories as half-open intervals. The healthy-weight band is the weight range at the same height that corresponds to BMI 18.5 through 24.9.
10
+
11
+ Check
12
+ C1 OK - the input was normalized into positive SI values.
13
+ C2 OK - height squared was reconstructed from the normalized height.
14
+ C3 OK - the BMI value matches the BMI = kg / m² formula.
15
+ C4 OK - a BMI of 18.49 stays below the normal-weight threshold.
16
+ C5 OK - the lower boundary is half-open: BMI 18.5 is classified as Normal.
17
+ C6 OK - BMI 25.0 starts the Overweight category.
18
+ C7 OK - BMI 30.0 starts the Obesity I category.
19
+ C8 OK - classification behavior is monotonic across representative BMI values.
20
+ C9 OK - the healthy-weight band was reconstructed from BMI 18.5 to 24.9 at the same height.
@@ -0,0 +1,23 @@
1
+ === Answer ===
2
+ In this toy PN-junction tunneling model, heavy doping narrows the depletion region enough for a tunneling window that rises to a peak and then falls, producing a negative-differential region.
3
+ case : pn-junction-tunneling
4
+ peak bias : 2
5
+ peak current proxy : 4
6
+ negative differential region : yes
7
+
8
+ === Reason Why ===
9
+ We model tunneling current as an exact overlap count between filled N-side states and empty P-side states while forward bias shifts the bands. Heavy doping is represented by a much narrower depletion region.
10
+ ordinary depletion width (nm) : 8
11
+ tunnel depletion width (nm) : 1
12
+ filled N-side states : [1, 2, 3, 4]
13
+ empty P-side states at 0 bias : [3, 4, 5, 6]
14
+ bias -> overlap current proxy : 0->2, 1->3, 2->4, 3->3, 4->2, 5->1, 6->0
15
+ peak point : 2 -> 4
16
+ high-bias point : 6 -> 0
17
+
18
+ === Check ===
19
+ heavily doped barrier is narrower : yes
20
+ peak occurs before overlap closes : yes
21
+ negative differential region present : yes
22
+ high-bias overlap closes : yes
23
+ peak equals full four-state overlap: yes
@@ -0,0 +1,42 @@
1
+ === Answer ===
2
+ The puzzle is solved, and the completed grid is the unique valid Sudoku solution.
3
+ case : sudoku
4
+ default puzzle : classic
5
+
6
+ Puzzle
7
+ 1 . . | . . 7 | . 9 .
8
+ . 3 . | . 2 . | . . 8
9
+ . . 9 | 6 . . | 5 . .
10
+
11
+ . . 5 | 3 . . | 9 . .
12
+ . 1 . | . 8 . | . . 2
13
+ 6 . . | . . 4 | . . .
14
+
15
+ 3 . . | . . . | . 1 .
16
+ . 4 . | . . . | . . 7
17
+ . . 7 | . . . | 3 . .
18
+
19
+ Completed grid
20
+ 1 6 2 | 8 5 7 | 4 9 3
21
+ 5 3 4 | 1 2 9 | 6 7 8
22
+ 7 8 9 | 6 4 3 | 5 2 1
23
+
24
+ 4 7 5 | 3 1 2 | 9 8 6
25
+ 9 1 3 | 5 8 6 | 7 4 2
26
+ 6 2 8 | 7 9 4 | 1 3 5
27
+
28
+ 3 5 6 | 4 7 8 | 2 1 9
29
+ 2 4 1 | 9 3 5 | 8 6 7
30
+ 8 9 7 | 2 6 1 | 3 5 4
31
+ === Reason Why ===
32
+ The solver starts from 23 clues and fills the remaining 58 cells by combining constraint propagation with depth-first search. At each step it chooses the empty cell with the fewest legal digits, places forced singles immediately, and only guesses when more than one candidate remains. Across the search it made 213 forced placements and tried 22 guesses, visited 23 search nodes overall, and backtracked 12 times before reaching the completed grid. The solver also confirmed that the solution is unique. Early steps: r2c3=4: guess, r5c3=3: forced, r2c1=5: guess, r2c4=1: guess, r2c6=9: forced, r2c7=6: guess, r2c8=7: forced, r1c7=4: guess, … and 50 more placements
33
+
34
+ === Check ===
35
+ C1 OK - every given clue is preserved in the final grid.
36
+ C2 OK - the final grid contains only digits 1 through 9, with no blanks left.
37
+ C3 OK - each row contains every digit exactly once.
38
+ C4 OK - each column contains every digit exactly once.
39
+ C5 OK - each 3×3 box contains every digit exactly once.
40
+ C6 OK - replaying the recorded placements from the original puzzle remains legal at every step.
41
+ C7 OK - the search statistics and the successful proof path are internally consistent.
42
+ C8 OK - a second search found no alternative solution, so the solution is unique.
@@ -0,0 +1,24 @@
1
+ === Answer ===
2
+ In this toy transistor-switch model, a low input leaves the transistor in cutoff (OFF) and a high input drives it into saturation (ON), so the load behaves like an on/off branch rather than a linear amplifier.
3
+ case : transistor-switch
4
+ low input state : cutoff / OFF
5
+ high input state : saturation / ON
6
+ on-state load current : 4.80 mA
7
+
8
+ === Reason Why ===
9
+ We model an NPN low-side switch with exact millivolt and microamp arithmetic. The base current comes from (Vin - Vbe,on)/Rb when the base-emitter junction is forward biased, and the collector current is the smaller of beta * Ib and the load-limited current (Vcc - Vce,sat)/Rl.
10
+ supply voltage : 5.00 V
11
+ base resistor : 10000 ohms
12
+ load resistor : 1000 ohms
13
+ transistor beta proxy : 100
14
+ low input : Vin=0.00 V -> Ib=0.00 mA, Ic=0.00 mA, Vce=5.00 V, state=cutoff / OFF
15
+ high input : Vin=5.00 V -> Ib=0.43 mA, Ic=4.80 mA, Vce=0.20 V, state=saturation / ON
16
+ high-input gain limit : 43.00 mA
17
+ high-input load limit : 4.80 mA
18
+
19
+ === Check ===
20
+ low input stays in cutoff : yes
21
+ high input reaches saturation : yes
22
+ switching states differ : yes
23
+ on-state current is load-limited : yes
24
+ load voltage matches resistor drop : yes
@@ -0,0 +1,227 @@
1
+ # ===========================================================================
2
+ # pn-junction-tunneling.n3
3
+ #
4
+ # - ordinary depletion width = 8 nm
5
+ # - tunnel depletion width = 1 nm
6
+ # - filled N-side states = [1, 2, 3, 4]
7
+ # - empty P-side states @ 0V = [3, 4, 5, 6]
8
+ # - bias points = 0..6
9
+ #
10
+ # For each bias, the empty P-side levels shift downward by the bias amount.
11
+ # A shifted level contributes 1 to the current proxy exactly when it lands in
12
+ # the occupied N-side window [1, 4]. The four slot contributions are summed.
13
+ # ===========================================================================
14
+
15
+ @prefix : <http://example.org/pn-junction-tunneling#> .
16
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
17
+ @prefix math: <http://www.w3.org/2000/10/swap/math#> .
18
+ @prefix list: <http://www.w3.org/2000/10/swap/list#> .
19
+ @prefix string: <http://www.w3.org/2000/10/swap/string#> .
20
+
21
+ # -----
22
+ # Facts
23
+ # -----
24
+
25
+ :case :name "pn-junction-tunneling" .
26
+ :case :ordinaryDepletionWidthNm 8 .
27
+ :case :tunnelDepletionWidthNm 1 .
28
+ :case :nFilledLevels (1 2 3 4) .
29
+ :case :pEmptyZeroBiasLevels (3 4 5 6) .
30
+
31
+ :b0 :value 0 .
32
+ :b1 :value 1 .
33
+ :b2 :value 2 .
34
+ :b3 :value 3 .
35
+ :b4 :value 4 .
36
+ :b5 :value 5 .
37
+ :b6 :value 6 .
38
+
39
+ # -----
40
+ # Logic
41
+ # -----
42
+
43
+ # Heavy doping is modeled as a much narrower depletion region.
44
+ { :case :tunnelDepletionWidthNm ?t .
45
+ :case :ordinaryDepletionWidthNm ?o .
46
+ ?t math:lessThan ?o .
47
+ }
48
+ => { :case :barrierIsNarrower true . } .
49
+
50
+ # For each bias node and each zero-bias P-side slot, shift the level downward:
51
+ # shifted = zeroBiasLevel - bias.
52
+ { ?bnode list:in (:b0 :b1 :b2 :b3 :b4 :b5 :b6) .
53
+ ?bnode :value ?bias .
54
+ :case :pEmptyZeroBiasLevels ?levels .
55
+ ?levels list:iterate ( ?slot ?zeroLevel ) .
56
+ ( ?zeroLevel ?bias ) math:difference ?shifted .
57
+ }
58
+ => { ( ?bnode ?slot ) :shiftedLevel ?shifted . } .
59
+
60
+ # Each shifted slot contributes 1 when it falls inside the occupied N-side
61
+ # window [1, 4], otherwise 0.
62
+ { ( ?bnode ?slot ) :shiftedLevel ?shifted .
63
+ ?shifted math:notLessThan 1 .
64
+ ?shifted math:notGreaterThan 4 .
65
+ }
66
+ => { ( ?bnode ?slot ) :overlapValue 1 . } .
67
+
68
+ { ( ?bnode ?slot ) :shiftedLevel ?shifted .
69
+ ?shifted math:lessThan 1 .
70
+ }
71
+ => { ( ?bnode ?slot ) :overlapValue 0 . } .
72
+
73
+ { ( ?bnode ?slot ) :shiftedLevel ?shifted .
74
+ ?shifted math:greaterThan 4 .
75
+ }
76
+ => { ( ?bnode ?slot ) :overlapValue 0 . } .
77
+
78
+ # The current proxy is the sum of the four slot contributions.
79
+ { ( ?bnode 0 ) :overlapValue ?v0 .
80
+ ( ?bnode 1 ) :overlapValue ?v1 .
81
+ ( ?bnode 2 ) :overlapValue ?v2 .
82
+ ( ?bnode 3 ) :overlapValue ?v3 .
83
+ ( ?v0 ?v1 ?v2 ?v3 ) math:sum ?current .
84
+ }
85
+ => { ?bnode :currentProxy ?current . } .
86
+
87
+ # Peak = the unique maximum at bias 2 in this toy curve.
88
+ { :b2 :currentProxy ?peak .
89
+ :b0 :currentProxy ?c0 . ?peak math:greaterThan ?c0 .
90
+ :b1 :currentProxy ?c1 . ?peak math:greaterThan ?c1 .
91
+ :b3 :currentProxy ?c3 . ?peak math:greaterThan ?c3 .
92
+ :b4 :currentProxy ?c4 . ?peak math:greaterThan ?c4 .
93
+ :b5 :currentProxy ?c5 . ?peak math:greaterThan ?c5 .
94
+ :b6 :currentProxy ?c6 . ?peak math:greaterThan ?c6 .
95
+ }
96
+ => { :case :peakBias 2 .
97
+ :case :peakCurrent ?peak . } .
98
+
99
+ # High-bias valley = the final sampled point, matching the Rust code.
100
+ { :b6 :currentProxy ?valley . }
101
+ => { :case :valleyBias 6 .
102
+ :case :valleyCurrent ?valley . } .
103
+
104
+ # A negative differential region is present once the curve drops after the peak.
105
+ { :b2 :currentProxy ?c2 .
106
+ :b3 :currentProxy ?c3 .
107
+ ?c2 math:greaterThan ?c3 .
108
+ }
109
+ => { :case :negativeDifferentialRegion true . } .
110
+
111
+ { :case :peakBias ?peakBias .
112
+ :case :valleyBias ?valleyBias .
113
+ ?peakBias math:lessThan ?valleyBias .
114
+ }
115
+ => { :case :peakIsBeforeValley true . } .
116
+
117
+ { :case :valleyCurrent 0 . }
118
+ => { :case :overlapClosesAtHighBias true . } .
119
+
120
+ { :case :peakCurrent ?peakCurrent .
121
+ :case :nFilledLevels ?nLevels .
122
+ ?nLevels list:length ?nCount .
123
+ ?peakCurrent math:equalTo ?nCount .
124
+ }
125
+ => { :case :peakMatchesFullOverlap true . } .
126
+
127
+ # -----------------------------------------------
128
+ # Presentation (ARC: Answer • Reason Why • Check)
129
+ # -----------------------------------------------
130
+
131
+ # Answer
132
+ { :case :peakBias ?peakBias .
133
+ :case :peakCurrent ?peakCurrent .
134
+ :case :negativeDifferentialRegion true .
135
+ ( "peak bias : %s\n" ?peakBias ) string:format ?peakBiasLine .
136
+ ( "peak current proxy : %s\n" ?peakCurrent ) string:format ?peakCurrentLine .
137
+ }
138
+ => {
139
+ :01-answer-1 log:outputString "=== Answer ===\n" .
140
+ :01-answer-2 log:outputString "In this toy PN-junction tunneling model, heavy doping narrows the depletion region enough for a tunneling window that rises to a peak and then falls, producing a negative-differential region.\n" .
141
+ :01-answer-3 log:outputString "case : pn-junction-tunneling\n" .
142
+ :01-answer-4 log:outputString ?peakBiasLine .
143
+ :01-answer-5 log:outputString ?peakCurrentLine .
144
+ :01-answer-6 log:outputString "negative differential region : yes\n\n" .
145
+ } .
146
+
147
+ # Reason Why
148
+ { :case :ordinaryDepletionWidthNm ?ordinary .
149
+ :case :tunnelDepletionWidthNm ?tunnel .
150
+ :b0 :currentProxy ?c0 .
151
+ :b1 :currentProxy ?c1 .
152
+ :b2 :currentProxy ?c2 .
153
+ :b3 :currentProxy ?c3 .
154
+ :b4 :currentProxy ?c4 .
155
+ :b5 :currentProxy ?c5 .
156
+ :b6 :currentProxy ?c6 .
157
+ :case :peakBias ?peakBias .
158
+ :case :peakCurrent ?peakCurrent .
159
+ :case :valleyBias ?valleyBias .
160
+ :case :valleyCurrent ?valleyCurrent .
161
+ ( "%s->%s, %s->%s, %s->%s, %s->%s, %s->%s, %s->%s, %s->%s"
162
+ 0 ?c0 1 ?c1 2 ?c2 3 ?c3 4 ?c4 5 ?c5 6 ?c6 ) string:format ?curve .
163
+ ( "ordinary depletion width (nm) : %s\n" ?ordinary ) string:format ?ordinaryLine .
164
+ ( "tunnel depletion width (nm) : %s\n" ?tunnel ) string:format ?tunnelLine .
165
+ ( "bias -> overlap current proxy : %s\n" ?curve ) string:format ?curveLine .
166
+ ( "peak point : %s -> %s\n" ?peakBias ?peakCurrent ) string:format ?peakLine .
167
+ ( "high-bias point : %s -> %s\n" ?valleyBias ?valleyCurrent ) string:format ?valleyLine .
168
+ }
169
+ => {
170
+ :02-why-1 log:outputString "=== Reason Why ===\n" .
171
+ :02-why-2 log:outputString "We model tunneling current as an exact overlap count between filled N-side states and empty P-side states while forward bias shifts the bands. Heavy doping is represented by a much narrower depletion region.\n" .
172
+ :02-why-3 log:outputString ?ordinaryLine .
173
+ :02-why-4 log:outputString ?tunnelLine .
174
+ :02-why-5 log:outputString "filled N-side states : [1, 2, 3, 4]\n" .
175
+ :02-why-6 log:outputString "empty P-side states at 0 bias : [3, 4, 5, 6]\n" .
176
+ :02-why-7 log:outputString ?curveLine .
177
+ :02-why-8 log:outputString ?peakLine .
178
+ :02-why-9 log:outputString ?valleyLine .
179
+ :02-why-99 log:outputString "\n" .
180
+ } .
181
+
182
+ # Check
183
+ { :case :barrierIsNarrower true .
184
+ :case :peakIsBeforeValley true .
185
+ :case :negativeDifferentialRegion true .
186
+ :case :overlapClosesAtHighBias true .
187
+ :case :peakMatchesFullOverlap true .
188
+ }
189
+ => {
190
+ :03-check-1 log:outputString "=== Check ===\n" .
191
+ :03-check-2 log:outputString "heavily doped barrier is narrower : yes\n" .
192
+ :03-check-3 log:outputString "peak occurs before overlap closes : yes\n" .
193
+ :03-check-4 log:outputString "negative differential region present : yes\n" .
194
+ :03-check-5 log:outputString "high-bias overlap closes : yes\n" .
195
+ :03-check-6 log:outputString "peak equals full four-state overlap: yes\n" .
196
+ } .
197
+
198
+ # ------------------
199
+ # Verification fuses
200
+ # ------------------
201
+
202
+ # If any of the key invariants fail, stop the run loudly.
203
+
204
+ { :case :tunnelDepletionWidthNm ?t .
205
+ :case :ordinaryDepletionWidthNm ?o .
206
+ ?t math:notLessThan ?o .
207
+ } => false .
208
+
209
+ { :case :peakBias ?peakBias .
210
+ :case :valleyBias ?valleyBias .
211
+ ?peakBias math:notLessThan ?valleyBias .
212
+ } => false .
213
+
214
+ { :b2 :currentProxy ?c2 .
215
+ :b3 :currentProxy ?c3 .
216
+ ?c2 math:notGreaterThan ?c3 .
217
+ } => false .
218
+
219
+ { :case :valleyCurrent ?valleyCurrent .
220
+ ?valleyCurrent math:notEqualTo 0 .
221
+ } => false .
222
+
223
+ { :case :peakCurrent ?peakCurrent .
224
+ :case :nFilledLevels ?nLevels .
225
+ ?nLevels list:length ?nCount .
226
+ ?peakCurrent math:notEqualTo ?nCount .
227
+ } => false .