eyeling 1.13.2 → 1.13.4

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,383 @@
1
+ # ================================================================================
2
+ # Dining Philosophers — Chandy–Misra (dirty/clean forks + requests)
3
+ #
4
+ # What this file demonstrates:
5
+ # A concrete distributed-style run (9 rounds) where each of 5 philosophers
6
+ # eats exactly 3 times (15 meals total), using the classic Chandy–Misra idea:
7
+ #
8
+ # - Forks are shared resources held by exactly one endpoint at a time.
9
+ # - Each fork is either Dirty or Clean.
10
+ # - Hungry philosophers request any adjacent fork they do not hold.
11
+ # - A holder transfers a fork ONLY if it is Dirty (the "dirty fork" rule).
12
+ # - Upon transfer, the receiver has it Clean.
13
+ # - After eating, forks become Dirty again (in this trace, that makes all
14
+ # forks Dirty at the end of each round).
15
+ #
16
+ # About the "trace construction" in N3:
17
+ # N3 reasoning is monotonic and does not have a built-in closed-world "else".
18
+ # To build a deterministic next state without negation, the trace includes
19
+ # explicit dp:KeepFork facts for forks that are not transferred in that round.
20
+ # Transfers themselves are NOT asserted: they are derived from Requests + Dirty.
21
+ #
22
+ # What you can query in a reasoner:
23
+ # - Meals: ?m a dp:Meal .
24
+ # - Requests: ?r a dp:Request .
25
+ # - Transfers: ?t a dp:SendFork .
26
+ #
27
+ # Derived meals match the "realistic schedule" pattern:
28
+ # Round 1/4/7: P1 and P3 eat
29
+ # Round 2/5/8: P2 and P4 eat
30
+ # Round 3/6/9: P5 eats
31
+ # ================================================================================
32
+
33
+ @prefix dp: <http://example.org/dp#> .
34
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
35
+
36
+ # ------------------
37
+ # Minimal vocabulary
38
+ # ------------------
39
+
40
+ dp:Philosopher a dp:Class .
41
+ dp:Fork a dp:Class .
42
+ dp:Slot a dp:Class .
43
+ dp:Config a dp:Class .
44
+
45
+ dp:ForkState a dp:Class .
46
+ dp:Request a dp:Class .
47
+ dp:SendFork a dp:Class .
48
+ dp:KeepFork a dp:Class .
49
+ dp:Meal a dp:Class .
50
+ dp:MealHandle a dp:Class .
51
+
52
+ dp:Dirty a dp:Cleanliness .
53
+ dp:Clean a dp:Cleanliness .
54
+ dp:Cleanliness a dp:Class .
55
+
56
+ dp:end1 a dp:Property .
57
+ dp:end2 a dp:Property .
58
+
59
+ dp:leftFork a dp:Property .
60
+ dp:rightFork a dp:Property .
61
+
62
+ dp:in a dp:Property . # state/action/message -> Config
63
+ dp:fork a dp:Property .
64
+ dp:holder a dp:Property .
65
+ dp:cleanliness a dp:Property .
66
+
67
+ dp:from a dp:Property .
68
+ dp:to a dp:Property .
69
+
70
+ dp:afterSends a dp:Property . # start-of-round Config -> after-transfer Config
71
+ dp:afterEat a dp:Property . # after-transfer Config -> end-of-round Config
72
+
73
+ dp:slot a dp:Property . # start-of-round Config -> Slot
74
+ dp:cycle a dp:Property . # start-of-round Config -> integer (meal number)
75
+ dp:hungry a dp:Property . # start-of-round Config -> Philosopher
76
+
77
+ dp:slotIndex a dp:Property . # Slot -> integer
78
+
79
+ dp:philosopher a dp:Property . # Meal -> Philosopher
80
+ dp:mealNo a dp:Property . # Meal -> integer
81
+ dp:inSlot a dp:Property . # Meal -> Slot
82
+ dp:usesFork a dp:Property . # Meal -> Fork
83
+
84
+ dp:meal a dp:Property . # MealHandle -> Meal
85
+
86
+ dp:ateTimes a dp:Property .
87
+ dp:Trace1 a dp:Thing .
88
+ dp:everyoneAteTimes3 a dp:Property .
89
+
90
+ # -------------------------------------------
91
+ # Topology: 5 philosophers, 5 forks in a ring
92
+ # -------------------------------------------
93
+
94
+ dp:P1 a dp:Philosopher .
95
+ dp:P2 a dp:Philosopher .
96
+ dp:P3 a dp:Philosopher .
97
+ dp:P4 a dp:Philosopher .
98
+ dp:P5 a dp:Philosopher .
99
+
100
+ dp:F12 a dp:Fork .
101
+ dp:F23 a dp:Fork .
102
+ dp:F34 a dp:Fork .
103
+ dp:F45 a dp:Fork .
104
+ dp:F51 a dp:Fork .
105
+
106
+ # Fork endpoints (ring)
107
+ dp:F12 dp:end1 dp:P1 ; dp:end2 dp:P2 .
108
+ dp:F23 dp:end1 dp:P2 ; dp:end2 dp:P3 .
109
+ dp:F34 dp:end1 dp:P3 ; dp:end2 dp:P4 .
110
+ dp:F45 dp:end1 dp:P4 ; dp:end2 dp:P5 .
111
+ dp:F51 dp:end1 dp:P5 ; dp:end2 dp:P1 .
112
+
113
+ # Choose a left/right convention around the ring
114
+ dp:P1 dp:leftFork dp:F51 ; dp:rightFork dp:F12 .
115
+ dp:P2 dp:leftFork dp:F12 ; dp:rightFork dp:F23 .
116
+ dp:P3 dp:leftFork dp:F23 ; dp:rightFork dp:F34 .
117
+ dp:P4 dp:leftFork dp:F34 ; dp:rightFork dp:F45 .
118
+ dp:P5 dp:leftFork dp:F45 ; dp:rightFork dp:F51 .
119
+
120
+ # ----------------
121
+ # Slots (9 rounds)
122
+ # ----------------
123
+
124
+ dp:s1 a dp:Slot ; dp:slotIndex 1 .
125
+ dp:s2 a dp:Slot ; dp:slotIndex 2 .
126
+ dp:s3 a dp:Slot ; dp:slotIndex 3 .
127
+ dp:s4 a dp:Slot ; dp:slotIndex 4 .
128
+ dp:s5 a dp:Slot ; dp:slotIndex 5 .
129
+ dp:s6 a dp:Slot ; dp:slotIndex 6 .
130
+ dp:s7 a dp:Slot ; dp:slotIndex 7 .
131
+ dp:s8 a dp:Slot ; dp:slotIndex 8 .
132
+ dp:s9 a dp:Slot ; dp:slotIndex 9 .
133
+
134
+ # ----------------------------------------------
135
+ # MealHandle mapping (stable URIs like dp:mP1_1)
136
+ # ----------------------------------------------
137
+
138
+ # P1
139
+ dp:hP1_1 a dp:MealHandle ; dp:philosopher dp:P1 ; dp:mealNo 1 ; dp:meal dp:mP1_1 .
140
+ dp:hP1_2 a dp:MealHandle ; dp:philosopher dp:P1 ; dp:mealNo 2 ; dp:meal dp:mP1_2 .
141
+ dp:hP1_3 a dp:MealHandle ; dp:philosopher dp:P1 ; dp:mealNo 3 ; dp:meal dp:mP1_3 .
142
+
143
+ # P2
144
+ dp:hP2_1 a dp:MealHandle ; dp:philosopher dp:P2 ; dp:mealNo 1 ; dp:meal dp:mP2_1 .
145
+ dp:hP2_2 a dp:MealHandle ; dp:philosopher dp:P2 ; dp:mealNo 2 ; dp:meal dp:mP2_2 .
146
+ dp:hP2_3 a dp:MealHandle ; dp:philosopher dp:P2 ; dp:mealNo 3 ; dp:meal dp:mP2_3 .
147
+
148
+ # P3
149
+ dp:hP3_1 a dp:MealHandle ; dp:philosopher dp:P3 ; dp:mealNo 1 ; dp:meal dp:mP3_1 .
150
+ dp:hP3_2 a dp:MealHandle ; dp:philosopher dp:P3 ; dp:mealNo 2 ; dp:meal dp:mP3_2 .
151
+ dp:hP3_3 a dp:MealHandle ; dp:philosopher dp:P3 ; dp:mealNo 3 ; dp:meal dp:mP3_3 .
152
+
153
+ # P4
154
+ dp:hP4_1 a dp:MealHandle ; dp:philosopher dp:P4 ; dp:mealNo 1 ; dp:meal dp:mP4_1 .
155
+ dp:hP4_2 a dp:MealHandle ; dp:philosopher dp:P4 ; dp:mealNo 2 ; dp:meal dp:mP4_2 .
156
+ dp:hP4_3 a dp:MealHandle ; dp:philosopher dp:P4 ; dp:mealNo 3 ; dp:meal dp:mP4_3 .
157
+
158
+ # P5
159
+ dp:hP5_1 a dp:MealHandle ; dp:philosopher dp:P5 ; dp:mealNo 1 ; dp:meal dp:mP5_1 .
160
+ dp:hP5_2 a dp:MealHandle ; dp:philosopher dp:P5 ; dp:mealNo 2 ; dp:meal dp:mP5_2 .
161
+ dp:hP5_3 a dp:MealHandle ; dp:philosopher dp:P5 ; dp:mealNo 3 ; dp:meal dp:mP5_3 .
162
+
163
+ # -------------------------------------------------------------------
164
+ # The 9-round hunger schedule (environment input)
165
+ #
166
+ # Start-of-round configs are: C0, C2, C4, C6, C8, C10, C12, C14, C16
167
+ # After-transfer configs are: C1, C3, C5, C7, C9, C11, C13, C15, C17
168
+ # End-of-round configs are: C2, C4, C6, C8, C10, C12, C14, C16, C18
169
+ # -------------------------------------------------------------------
170
+
171
+ dp:C0 a dp:Config ; dp:slot dp:s1 ; dp:cycle 1 ; dp:hungry dp:P1, dp:P3 ; dp:afterSends dp:C1 .
172
+ dp:C1 a dp:Config ; dp:afterEat dp:C2 .
173
+ dp:C2 a dp:Config ; dp:slot dp:s2 ; dp:cycle 1 ; dp:hungry dp:P2, dp:P4 ; dp:afterSends dp:C3 .
174
+ dp:C3 a dp:Config ; dp:afterEat dp:C4 .
175
+ dp:C4 a dp:Config ; dp:slot dp:s3 ; dp:cycle 1 ; dp:hungry dp:P5 ; dp:afterSends dp:C5 .
176
+ dp:C5 a dp:Config ; dp:afterEat dp:C6 .
177
+ dp:C6 a dp:Config ; dp:slot dp:s4 ; dp:cycle 2 ; dp:hungry dp:P1, dp:P3 ; dp:afterSends dp:C7 .
178
+ dp:C7 a dp:Config ; dp:afterEat dp:C8 .
179
+ dp:C8 a dp:Config ; dp:slot dp:s5 ; dp:cycle 2 ; dp:hungry dp:P2, dp:P4 ; dp:afterSends dp:C9 .
180
+ dp:C9 a dp:Config ; dp:afterEat dp:C10 .
181
+ dp:C10 a dp:Config ; dp:slot dp:s6 ; dp:cycle 2 ; dp:hungry dp:P5 ; dp:afterSends dp:C11 .
182
+ dp:C11 a dp:Config ; dp:afterEat dp:C12 .
183
+ dp:C12 a dp:Config ; dp:slot dp:s7 ; dp:cycle 3 ; dp:hungry dp:P1, dp:P3 ; dp:afterSends dp:C13 .
184
+ dp:C13 a dp:Config ; dp:afterEat dp:C14 .
185
+ dp:C14 a dp:Config ; dp:slot dp:s8 ; dp:cycle 3 ; dp:hungry dp:P2, dp:P4 ; dp:afterSends dp:C15 .
186
+ dp:C15 a dp:Config ; dp:afterEat dp:C16 .
187
+ dp:C16 a dp:Config ; dp:slot dp:s9 ; dp:cycle 3 ; dp:hungry dp:P5 ; dp:afterSends dp:C17 .
188
+ dp:C17 a dp:Config ; dp:afterEat dp:C18 .
189
+ dp:C18 a dp:Config .
190
+
191
+ # ---------------------------------------------------------------------
192
+ # Initial fork ownership (C0) — all forks Dirty
193
+ # Orientation breaks symmetry (one endpoint initially holds each fork).
194
+ # ---------------------------------------------------------------------
195
+
196
+ [ a dp:ForkState ; dp:in dp:C0 ; dp:fork dp:F12 ; dp:holder dp:P1 ; dp:cleanliness dp:Dirty ] .
197
+ [ a dp:ForkState ; dp:in dp:C0 ; dp:fork dp:F23 ; dp:holder dp:P2 ; dp:cleanliness dp:Dirty ] .
198
+ [ a dp:ForkState ; dp:in dp:C0 ; dp:fork dp:F34 ; dp:holder dp:P3 ; dp:cleanliness dp:Dirty ] .
199
+ [ a dp:ForkState ; dp:in dp:C0 ; dp:fork dp:F45 ; dp:holder dp:P4 ; dp:cleanliness dp:Dirty ] .
200
+ [ a dp:ForkState ; dp:in dp:C0 ; dp:fork dp:F51 ; dp:holder dp:P1 ; dp:cleanliness dp:Dirty ] .
201
+
202
+ # -------------------------------------------------------------------------------
203
+ # KeepFork facts (the "no transfer for this fork in this round" part)
204
+ # Only listed for forks that are NOT transferred; transfers are derived by rules.
205
+ # -------------------------------------------------------------------------------
206
+
207
+ # Round 1 (C0): only F23 transfers -> keep F12,F34,F45,F51
208
+ [ a dp:KeepFork ; dp:in dp:C0 ; dp:fork dp:F12 ] .
209
+ [ a dp:KeepFork ; dp:in dp:C0 ; dp:fork dp:F34 ] .
210
+ [ a dp:KeepFork ; dp:in dp:C0 ; dp:fork dp:F45 ] .
211
+ [ a dp:KeepFork ; dp:in dp:C0 ; dp:fork dp:F51 ] .
212
+
213
+ # Round 2 (C2): transfers F12,F23,F34 -> keep F45,F51
214
+ [ a dp:KeepFork ; dp:in dp:C2 ; dp:fork dp:F45 ] .
215
+ [ a dp:KeepFork ; dp:in dp:C2 ; dp:fork dp:F51 ] .
216
+
217
+ # Round 3 (C4): transfers F45,F51 -> keep F12,F23,F34
218
+ [ a dp:KeepFork ; dp:in dp:C4 ; dp:fork dp:F12 ] .
219
+ [ a dp:KeepFork ; dp:in dp:C4 ; dp:fork dp:F23 ] .
220
+ [ a dp:KeepFork ; dp:in dp:C4 ; dp:fork dp:F34 ] .
221
+
222
+ # Round 4 (C6): transfers F51,F12,F23,F34 -> keep F45
223
+ [ a dp:KeepFork ; dp:in dp:C6 ; dp:fork dp:F45 ] .
224
+
225
+ # Round 5 (C8): transfers F12,F23,F34,F45 -> keep F51
226
+ [ a dp:KeepFork ; dp:in dp:C8 ; dp:fork dp:F51 ] .
227
+
228
+ # Round 6 (C10): transfers F45,F51 -> keep F12,F23,F34
229
+ [ a dp:KeepFork ; dp:in dp:C10 ; dp:fork dp:F12 ] .
230
+ [ a dp:KeepFork ; dp:in dp:C10 ; dp:fork dp:F23 ] .
231
+ [ a dp:KeepFork ; dp:in dp:C10 ; dp:fork dp:F34 ] .
232
+
233
+ # Round 7 (C12): transfers F51,F12,F23,F34 -> keep F45
234
+ [ a dp:KeepFork ; dp:in dp:C12 ; dp:fork dp:F45 ] .
235
+
236
+ # Round 8 (C14): transfers F12,F23,F34,F45 -> keep F51
237
+ [ a dp:KeepFork ; dp:in dp:C14 ; dp:fork dp:F51 ] .
238
+
239
+ # Round 9 (C16): transfers F45,F51 -> keep F12,F23,F34
240
+ [ a dp:KeepFork ; dp:in dp:C16 ; dp:fork dp:F12 ] .
241
+ [ a dp:KeepFork ; dp:in dp:C16 ; dp:fork dp:F23 ] .
242
+ [ a dp:KeepFork ; dp:in dp:C16 ; dp:fork dp:F34 ] .
243
+
244
+ # ------------------
245
+ # Chandy–Misra rules
246
+ # ------------------
247
+
248
+ # R1: Hungry philosophers request any adjacent fork they do not hold.
249
+ # (We do left and right separately for clarity.)
250
+
251
+ {
252
+ ?C a dp:Config .
253
+ ?C dp:hungry ?P .
254
+ ?P dp:leftFork ?F .
255
+ [ a dp:ForkState ; dp:in ?C ; dp:fork ?F ; dp:holder ?Q ] .
256
+ ?Q log:notEqualTo ?P .
257
+ }
258
+ =>
259
+ {
260
+ [ a dp:Request ; dp:in ?C ; dp:from ?P ; dp:to ?Q ; dp:fork ?F ] .
261
+ } .
262
+
263
+ {
264
+ ?C a dp:Config .
265
+ ?C dp:hungry ?P .
266
+ ?P dp:rightFork ?F .
267
+ [ a dp:ForkState ; dp:in ?C ; dp:fork ?F ; dp:holder ?Q ] .
268
+ ?Q log:notEqualTo ?P .
269
+ }
270
+ =>
271
+ {
272
+ [ a dp:Request ; dp:in ?C ; dp:from ?P ; dp:to ?Q ; dp:fork ?F ] .
273
+ } .
274
+
275
+ # R2: Dirty-fork rule: if a fork is Dirty and requested, it is sent.
276
+ # (In the classic algorithm, the holder does not send while actually eating.
277
+ # This trace models sends happening in the "start-of-round" config, before meals.)
278
+
279
+ {
280
+ [ a dp:Request ; dp:in ?C ; dp:from ?P ; dp:to ?Q ; dp:fork ?F ] .
281
+ [ a dp:ForkState ; dp:in ?C ; dp:fork ?F ; dp:holder ?Q ; dp:cleanliness dp:Dirty ] .
282
+ }
283
+ =>
284
+ {
285
+ [ a dp:SendFork ; dp:in ?C ; dp:from ?Q ; dp:to ?P ; dp:fork ?F ] .
286
+ } .
287
+
288
+ # ----------------------------------------------
289
+ # State update: start-of-round -> after-transfer
290
+ # ----------------------------------------------
291
+
292
+ # U1: Apply transfers: after-transfer holder is the receiver; the fork becomes Clean.
293
+ {
294
+ ?C dp:afterSends ?CS .
295
+ [ a dp:SendFork ; dp:in ?C ; dp:fork ?F ; dp:to ?P ] .
296
+ }
297
+ =>
298
+ {
299
+ [ a dp:ForkState ; dp:in ?CS ; dp:fork ?F ; dp:holder ?P ; dp:cleanliness dp:Clean ] .
300
+ } .
301
+
302
+ # U2: Apply keeps: if we declare KeepFork for a fork in a round, copy its state forward.
303
+ {
304
+ ?C dp:afterSends ?CS .
305
+ [ a dp:KeepFork ; dp:in ?C ; dp:fork ?F ] .
306
+ [ a dp:ForkState ; dp:in ?C ; dp:fork ?F ; dp:holder ?H ; dp:cleanliness ?CL ] .
307
+ }
308
+ =>
309
+ {
310
+ [ a dp:ForkState ; dp:in ?CS ; dp:fork ?F ; dp:holder ?H ; dp:cleanliness ?CL ] .
311
+ } .
312
+
313
+ # ----------------------------------------------------------------------
314
+ # Meals: after-transfer, a hungry philosopher who holds both forks eats.
315
+ # ----------------------------------------------------------------------
316
+
317
+ {
318
+ ?C dp:afterSends ?CS .
319
+ ?C dp:slot ?S ; dp:cycle ?N .
320
+ ?C dp:hungry ?P .
321
+
322
+ ?H a dp:MealHandle ; dp:philosopher ?P ; dp:mealNo ?N ; dp:meal ?M .
323
+
324
+ ?P dp:leftFork ?LF ; dp:rightFork ?RF .
325
+
326
+ [ a dp:ForkState ; dp:in ?CS ; dp:fork ?LF ; dp:holder ?P ] .
327
+ [ a dp:ForkState ; dp:in ?CS ; dp:fork ?RF ; dp:holder ?P ] .
328
+ }
329
+ =>
330
+ {
331
+ ?M a dp:Meal ;
332
+ dp:philosopher ?P ;
333
+ dp:mealNo ?N ;
334
+ dp:inSlot ?S ;
335
+ dp:usesFork ?LF, ?RF .
336
+ } .
337
+
338
+ # ----------------------------------------------------------------------------
339
+ # State update: after-transfer -> end-of-round
340
+ #
341
+ # In this particular 9-round trace, every transferred fork is immediately used
342
+ # by an eater in that round, and kept forks were already Dirty.
343
+ # So at end-of-round, all forks are Dirty again.
344
+ # ----------------------------------------------------------------------------
345
+
346
+ # U3: End-of-round holders are the same as after-transfer holders.
347
+ # (Forks stay with whoever has them until requested in a later round.)
348
+ {
349
+ ?CS dp:afterEat ?CE .
350
+ [ a dp:ForkState ; dp:in ?CS ; dp:fork ?F ; dp:holder ?H ] .
351
+ }
352
+ =>
353
+ {
354
+ [ a dp:ForkState ; dp:in ?CE ; dp:fork ?F ; dp:holder ?H ; dp:cleanliness dp:Dirty ] .
355
+ } .
356
+
357
+ # -------------------------------------------------
358
+ # Derived summaries: "each philosopher ate 3 times"
359
+ # -------------------------------------------------
360
+
361
+ {
362
+ ?P a dp:Philosopher .
363
+ ?M1 a dp:Meal ; dp:philosopher ?P ; dp:mealNo 1 .
364
+ ?M2 a dp:Meal ; dp:philosopher ?P ; dp:mealNo 2 .
365
+ ?M3 a dp:Meal ; dp:philosopher ?P ; dp:mealNo 3 .
366
+ }
367
+ =>
368
+ {
369
+ ?P dp:ateTimes 3 .
370
+ } .
371
+
372
+ {
373
+ dp:P1 dp:ateTimes 3 .
374
+ dp:P2 dp:ateTimes 3 .
375
+ dp:P3 dp:ateTimes 3 .
376
+ dp:P4 dp:ateTimes 3 .
377
+ dp:P5 dp:ateTimes 3 .
378
+ }
379
+ =>
380
+ {
381
+ dp:Trace1 dp:everyoneAteTimes3 true .
382
+ } .
383
+
@@ -1,4 +1,6 @@
1
- VERSION "1.2"
1
+ VERSION "1.2"
2
+
2
3
  PREFIX : <http://example.com/>
3
4
  PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
5
+
4
6
  :a :name "Alice" ~ :t {| :statedBy :bob ; :recorded "2021-07-07"^^xsd:date |} .
@@ -1,3 +1,5 @@
1
+ VERSION "1.2"
2
+
1
3
  PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
2
4
  PREFIX : <http://www.example.org/>
3
5
 
@@ -1,7 +1,8 @@
1
1
  VERSION "1.2"
2
+
2
3
  PREFIX : <http://www.example.org/>
3
4
  PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
4
5
 
5
- _:e38 :familyName "Smith" .
6
+ _:e38 :familyName "Smith" .
6
7
  _:anno rdf:reifies <<( _:e38 :jobTitle "Designer" )>> .
7
- _:anno :accordingTo _:e22 .
8
+ _:anno :accordingTo _:e22 .
@@ -0,0 +1,205 @@
1
+ # =========================================================================
2
+ # Kaprekar’s routine for 4-digit numbers
3
+ #
4
+ # What it does
5
+ # Enumerates all 4-digit numbers 0000..9999 (leading zeros allowed),
6
+ # applies the Kaprekar step:
7
+ # - take the 4 digits of n
8
+ # - sort digits descending to form D, ascending to form A
9
+ # - next = D − A
10
+ # and emits triples of the form:
11
+ # 3524 :kaprekar (3087 8352 6174) .
12
+ #
13
+ # Output convention
14
+ # The list contains the successive results starting from the *first* step
15
+ # and ends at 6174 (Kaprekar’s constant).
16
+ #
17
+ # Skipping the repdigit → 0000 basin
18
+ # Starts that eventually reach 0000 (e.g., 1111, 2222, … and anything
19
+ # that falls into that basin) can be omitted by adding constraints that
20
+ # require the chain to end in 6174.
21
+ #
22
+ # Performance notes
23
+ # - The step computation can be sped up by using the identity:
24
+ # next = 999*(a3-a0) + 90*(a2-a1)
25
+ # where a0<=a1<=a2<=a3 are the digits sorted ascending.
26
+ # This avoids explicitly constructing D and A as 4-digit numbers.
27
+ # - For 4-digit Kaprekar, the maximum number of steps to reach 6174 is 7,
28
+ # so the path rules can be unrolled to a fixed bound (no recursion),
29
+ # which improves termination and speed.
30
+ #
31
+ # Predicates used (typical)
32
+ # :kaprekar — final output predicate: start -> (sequence ... 6174)
33
+ # kap:step — internal: n -> next for one Kaprekar step
34
+ # =========================================================================
35
+
36
+ @prefix : <http://example.org/#>.
37
+ @prefix kap: <http://example.org/kaprekar#>.
38
+ @prefix math: <http://www.w3.org/2000/10/swap/math#>.
39
+ @prefix list: <http://www.w3.org/2000/10/swap/list#>.
40
+ @prefix log: <http://www.w3.org/2000/10/swap/log#>.
41
+
42
+ # Define the digit list ONCE (avoid repeating a list literal 4 times)
43
+ :digitList :items (0 1 2 3 4 5 6 7 8 9).
44
+
45
+ # Output: only :kaprekar triples
46
+ { ?n :kaprekar ?seq. } log:query { ?n :kaprekar ?seq. }.
47
+
48
+ # -------------------------------------------------------
49
+ # Precompute kap:step for all 0000..9999.
50
+ # Uses identity: if sorted digits are a0<=a1<=a2<=a3 then
51
+ # desc-asc = 999*(a3-a0) + 90*(a2-a1)
52
+ # -------------------------------------------------------
53
+
54
+ {
55
+ :digitList :items ?L.
56
+
57
+ ?L list:member ?d1.
58
+ ?L list:member ?d2.
59
+ ?L list:member ?d3.
60
+ ?L list:member ?d4.
61
+
62
+ # build n = 1000*d1 + 100*d2 + 10*d3 + d4
63
+ (?d1 1000) math:product ?p1.
64
+ (?d2 100) math:product ?p2.
65
+ (?d3 10) math:product ?p3.
66
+ (?d4 1) math:product ?p4.
67
+ (?p1 ?p2 ?p3 ?p4) math:sum ?n.
68
+
69
+ # sort digits once
70
+ (?d1 ?d2 ?d3 ?d4) list:sort ?asc.
71
+
72
+ # unpack a0 a1 a2 a3
73
+ ?asc list:firstRest (?a0 ?r0).
74
+ ?r0 list:firstRest (?a1 ?r1).
75
+ ?r1 list:firstRest (?a2 ?r2).
76
+ ?r2 list:firstRest (?a3 ()).
77
+
78
+ # next = 999*(a3-a0) + 90*(a2-a1)
79
+ (?a3 ?a0) math:difference ?x.
80
+ (?a2 ?a1) math:difference ?y.
81
+ (?x 999) math:product ?px.
82
+ (?y 90) math:product ?py.
83
+ (?px ?py) math:sum ?next.
84
+ } => {
85
+ ?n kap:step ?next.
86
+ }.
87
+
88
+ # ------------------------------------------------------------------
89
+ # Emit only chains that end in 6174 (skip the repdigit->0 basin)
90
+ # Maximum steps to 6174 is 7, so we unroll 1..7.
91
+ #
92
+ # Convention: output list includes each intermediate INCLUDING 6174.
93
+ # Example: 3524 -> (3087 8352 6174)
94
+ # ------------------------------------------------------------------
95
+
96
+ # n = 6174 itself (optional; keep if you want it in output)
97
+ 6174 :kaprekar (6174).
98
+
99
+ # 1 step
100
+ {
101
+ ?n log:notEqualTo 6174.
102
+ ?n kap:step 6174.
103
+ # exclude repdigits/0 basin:
104
+ ?n log:notEqualTo 0.
105
+ } => {
106
+ ?n :kaprekar (6174).
107
+ }.
108
+
109
+ # 2 steps
110
+ {
111
+ ?n log:notEqualTo 6174.
112
+ ?n log:notEqualTo 0.
113
+ ?n kap:step ?s1.
114
+ ?s1 log:notEqualTo 0.
115
+ ?s1 log:notEqualTo 6174.
116
+ ?s1 kap:step 6174.
117
+ } => {
118
+ ?n :kaprekar (?s1 6174).
119
+ }.
120
+
121
+ # 3 steps
122
+ {
123
+ ?n log:notEqualTo 6174.
124
+ ?n log:notEqualTo 0.
125
+ ?n kap:step ?s1.
126
+ ?s1 log:notEqualTo 0. ?s1 log:notEqualTo 6174.
127
+ ?s1 kap:step ?s2.
128
+ ?s2 log:notEqualTo 0. ?s2 log:notEqualTo 6174.
129
+ ?s2 kap:step 6174.
130
+ } => {
131
+ ?n :kaprekar (?s1 ?s2 6174).
132
+ }.
133
+
134
+ # 4 steps
135
+ {
136
+ ?n log:notEqualTo 6174.
137
+ ?n log:notEqualTo 0.
138
+ ?n kap:step ?s1.
139
+ ?s1 log:notEqualTo 0. ?s1 log:notEqualTo 6174.
140
+ ?s1 kap:step ?s2.
141
+ ?s2 log:notEqualTo 0. ?s2 log:notEqualTo 6174.
142
+ ?s2 kap:step ?s3.
143
+ ?s3 log:notEqualTo 0. ?s3 log:notEqualTo 6174.
144
+ ?s3 kap:step 6174.
145
+ } => {
146
+ ?n :kaprekar (?s1 ?s2 ?s3 6174).
147
+ }.
148
+
149
+ # 5 steps
150
+ {
151
+ ?n log:notEqualTo 6174.
152
+ ?n log:notEqualTo 0.
153
+ ?n kap:step ?s1.
154
+ ?s1 log:notEqualTo 0. ?s1 log:notEqualTo 6174.
155
+ ?s1 kap:step ?s2.
156
+ ?s2 log:notEqualTo 0. ?s2 log:notEqualTo 6174.
157
+ ?s2 kap:step ?s3.
158
+ ?s3 log:notEqualTo 0. ?s3 log:notEqualTo 6174.
159
+ ?s3 kap:step ?s4.
160
+ ?s4 log:notEqualTo 0. ?s4 log:notEqualTo 6174.
161
+ ?s4 kap:step 6174.
162
+ } => {
163
+ ?n :kaprekar (?s1 ?s2 ?s3 ?s4 6174).
164
+ }.
165
+
166
+ # 6 steps
167
+ {
168
+ ?n log:notEqualTo 6174.
169
+ ?n log:notEqualTo 0.
170
+ ?n kap:step ?s1.
171
+ ?s1 log:notEqualTo 0. ?s1 log:notEqualTo 6174.
172
+ ?s1 kap:step ?s2.
173
+ ?s2 log:notEqualTo 0. ?s2 log:notEqualTo 6174.
174
+ ?s2 kap:step ?s3.
175
+ ?s3 log:notEqualTo 0. ?s3 log:notEqualTo 6174.
176
+ ?s3 kap:step ?s4.
177
+ ?s4 log:notEqualTo 0. ?s4 log:notEqualTo 6174.
178
+ ?s4 kap:step ?s5.
179
+ ?s5 log:notEqualTo 0. ?s5 log:notEqualTo 6174.
180
+ ?s5 kap:step 6174.
181
+ } => {
182
+ ?n :kaprekar (?s1 ?s2 ?s3 ?s4 ?s5 6174).
183
+ }.
184
+
185
+ # 7 steps (max)
186
+ {
187
+ ?n log:notEqualTo 6174.
188
+ ?n log:notEqualTo 0.
189
+ ?n kap:step ?s1.
190
+ ?s1 log:notEqualTo 0. ?s1 log:notEqualTo 6174.
191
+ ?s1 kap:step ?s2.
192
+ ?s2 log:notEqualTo 0. ?s2 log:notEqualTo 6174.
193
+ ?s2 kap:step ?s3.
194
+ ?s3 log:notEqualTo 0. ?s3 log:notEqualTo 6174.
195
+ ?s3 kap:step ?s4.
196
+ ?s4 log:notEqualTo 0. ?s4 log:notEqualTo 6174.
197
+ ?s4 kap:step ?s5.
198
+ ?s5 log:notEqualTo 0. ?s5 log:notEqualTo 6174.
199
+ ?s5 kap:step ?s6.
200
+ ?s6 log:notEqualTo 0. ?s6 log:notEqualTo 6174.
201
+ ?s6 kap:step 6174.
202
+ } => {
203
+ ?n :kaprekar (?s1 ?s2 ?s3 ?s4 ?s5 ?s6 6174).
204
+ }.
205
+