kibi-core 0.24.0 → 0.26.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.
- package/package.json +1 -1
- package/schema/validation.pl +3 -1
- package/src/certificate_check.pl +38 -4
- package/src/certificates.pl +10 -2
- package/src/claim_obligations.pl +356 -28
- package/src/code_certificates.pl +239 -4
- package/src/kb.pl +49 -2
- package/src/model_check.pl +728 -0
- package/src/requirement_proof.pl +10 -3
- package/src/rule_registry.pl +1 -1
package/src/code_certificates.pl
CHANGED
|
@@ -19,6 +19,22 @@
|
|
|
19
19
|
% plus the negated claim admit no value.
|
|
20
20
|
% smt The query asks (check-sat) and the solver answered unsat.
|
|
21
21
|
% (Kibi re-runs the solver on the query at ingest.)
|
|
22
|
+
% rule Every domain lists distinct values, every predicate table
|
|
23
|
+
% holds exactly one boolean for every combination of its
|
|
24
|
+
% argument domains, and no call failed. (Kibi recomputes the
|
|
25
|
+
% domains and tables from the code at ingest.)
|
|
26
|
+
% state_machine
|
|
27
|
+
% The machine lists distinct states, and its transitions and
|
|
28
|
+
% initial states are pairs and states of that list. (Kibi
|
|
29
|
+
% reads the machine from the code at ingest.)
|
|
30
|
+
% regex The certificate holds the code's pattern (source, flags and
|
|
31
|
+
% flavor) or string constant. (Kibi reads it from the code
|
|
32
|
+
% at ingest and decides the claim's language exactly.)
|
|
33
|
+
% source The certificate names a project source (a reader and its
|
|
34
|
+
% spec) and holds what Kibi read from it: the members as a
|
|
35
|
+
% sorted list of distinct strings, and for schema, validator
|
|
36
|
+
% and route readers the tables, objects or routes. (Kibi
|
|
37
|
+
% reads the source itself at ingest and stores its own read.)
|
|
22
38
|
%
|
|
23
39
|
% The digest a receipt records is the sha256 of the certificate's canonical
|
|
24
40
|
% JSON as canonical_json/2 below spells it. The CLI records the digest this
|
|
@@ -35,6 +51,7 @@
|
|
|
35
51
|
:- use_module(library(crypto)).
|
|
36
52
|
:- use_module(library(apply)).
|
|
37
53
|
:- use_module(library(lists)).
|
|
54
|
+
:- use_module(library(yall)).
|
|
38
55
|
:- use_module('intervals.pl', [
|
|
39
56
|
numeric_constraints_satisfiable/2,
|
|
40
57
|
numeric_constraint_entailed/3
|
|
@@ -90,8 +107,8 @@ certificate_problem(Certificate, Reason) :-
|
|
|
90
107
|
),
|
|
91
108
|
( kind_problem(Kind, Certificate, Reason0)
|
|
92
109
|
-> Reason = Reason0
|
|
93
|
-
; \+ memberchk(Kind, [exhaustive, typecheck, arithmetic, smt])
|
|
94
|
-
-> Reason = "the certificate kind is not exhaustive, typecheck, arithmetic or
|
|
110
|
+
; \+ memberchk(Kind, [exhaustive, typecheck, arithmetic, smt, rule, state_machine, regex, source])
|
|
111
|
+
-> Reason = "the certificate kind is not exhaustive, typecheck, arithmetic, smt, rule, state_machine, regex or source"
|
|
95
112
|
).
|
|
96
113
|
|
|
97
114
|
kind_problem(exhaustive, Certificate, Reason) :-
|
|
@@ -164,9 +181,18 @@ kind_problem(arithmetic, Certificate, Reason) :-
|
|
|
164
181
|
arithmetic_constraint(X, Claim, Goal),
|
|
165
182
|
integer_variables(Type, X, Integers),
|
|
166
183
|
( \+ numeric_constraints_satisfiable(Premises, Integers)
|
|
167
|
-
->
|
|
184
|
+
-> maplist(constraint_text(Variable), Assumptions, Texts),
|
|
185
|
+
atomic_list_concat(Texts, ' and ', Joined),
|
|
186
|
+
format(string(Reason),
|
|
187
|
+
"the assumptions contradict each other (~w), so no value satisfies them and they prove nothing about the claim",
|
|
188
|
+
[Joined])
|
|
168
189
|
; \+ numeric_constraint_entailed(Premises, Goal, Integers)
|
|
169
|
-
->
|
|
190
|
+
-> ( arithmetic_counterexample(Type, Assumptions, Claim, Value)
|
|
191
|
+
-> format(string(Reason),
|
|
192
|
+
"the assumptions do not entail the claim: ~w = ~w satisfies them and breaks it",
|
|
193
|
+
[Variable, Value])
|
|
194
|
+
; Reason = "the assumptions do not entail the claim: some value satisfies them and breaks it"
|
|
195
|
+
)
|
|
170
196
|
)
|
|
171
197
|
).
|
|
172
198
|
kind_problem(smt, Certificate, Reason) :-
|
|
@@ -185,6 +211,170 @@ kind_problem(smt, Certificate, Reason) :-
|
|
|
185
211
|
-> Reason = "the solver did not answer unsat"
|
|
186
212
|
).
|
|
187
213
|
|
|
214
|
+
kind_problem(rule, Certificate, Reason) :-
|
|
215
|
+
field(Certificate, domains, Domains),
|
|
216
|
+
field(Certificate, predicates, Predicates),
|
|
217
|
+
( \+ (is_dict(Domains), dict_pairs(Domains, _, DomainPairs), DomainPairs \== [])
|
|
218
|
+
-> Reason = "the certificate has no domains"
|
|
219
|
+
; dict_pairs(Domains, _, DomainPairs),
|
|
220
|
+
member(Type-Domain, DomainPairs),
|
|
221
|
+
field(Domain, members, Members),
|
|
222
|
+
\+ distinct_strings(Members)
|
|
223
|
+
-> format(string(Reason), "the domain ~w must list distinct values, at least one", [Type])
|
|
224
|
+
; \+ (is_dict(Predicates), dict_pairs(Predicates, _, PredicatePairs), PredicatePairs \== [])
|
|
225
|
+
-> Reason = "the certificate has no predicate tables"
|
|
226
|
+
; dict_pairs(Predicates, _, PredicatePairs),
|
|
227
|
+
member(Name-Table, PredicatePairs),
|
|
228
|
+
table_problem(Name, Table, Domains, Reason0)
|
|
229
|
+
-> Reason = Reason0
|
|
230
|
+
).
|
|
231
|
+
kind_problem(state_machine, Certificate, Reason) :-
|
|
232
|
+
field(Certificate, machine, Machine),
|
|
233
|
+
field(Machine, symbol_id, Symbol),
|
|
234
|
+
field(Machine, states, States),
|
|
235
|
+
field(Machine, transitions, Transitions),
|
|
236
|
+
( \+ is_dict(Machine)
|
|
237
|
+
-> Reason = "the certificate has no machine"
|
|
238
|
+
; \+ nonempty_string(Symbol)
|
|
239
|
+
-> Reason = "the machine names no symbol_id"
|
|
240
|
+
; \+ distinct_strings(States)
|
|
241
|
+
-> Reason = "the machine must list distinct states, at least one"
|
|
242
|
+
; \+ is_list(Transitions)
|
|
243
|
+
-> Reason = "the machine has no transitions list"
|
|
244
|
+
; member(Edge, Transitions),
|
|
245
|
+
\+ ( is_list(Edge), Edge = [From, To], memberchk(From, States), memberchk(To, States) )
|
|
246
|
+
-> Reason = "every transition must be a [from, to] pair of the machine's states"
|
|
247
|
+
; msort(Transitions, SortedEdges), sort(Transitions, UniqueEdges), SortedEdges \== UniqueEdges
|
|
248
|
+
-> Reason = "the machine lists a transition twice"
|
|
249
|
+
; get_dict(initial, Machine, Initial),
|
|
250
|
+
\+ ( is_list(Initial), forall(member(S, Initial), memberchk(S, States)) )
|
|
251
|
+
-> Reason = "the machine's initial states must be states of the machine"
|
|
252
|
+
).
|
|
253
|
+
kind_problem(regex, Certificate, Reason) :-
|
|
254
|
+
field(Certificate, pattern, Pattern),
|
|
255
|
+
field(Pattern, symbol_id, Symbol),
|
|
256
|
+
field(Pattern, type, Type),
|
|
257
|
+
( \+ is_dict(Pattern)
|
|
258
|
+
-> Reason = "the certificate has no pattern"
|
|
259
|
+
; \+ nonempty_string(Symbol)
|
|
260
|
+
-> Reason = "the pattern names no symbol_id"
|
|
261
|
+
; Type == "regexp"
|
|
262
|
+
-> field(Pattern, source, Source),
|
|
263
|
+
field(Pattern, flags, Flags),
|
|
264
|
+
field(Pattern, flavor, Flavor),
|
|
265
|
+
( \+ (string(Source), string(Flags))
|
|
266
|
+
-> Reason = "a regexp pattern must give its source and flags"
|
|
267
|
+
; \+ memberchk(Flavor, ["js", "python"])
|
|
268
|
+
-> Reason = "a regexp pattern's flavor must be js or python"
|
|
269
|
+
)
|
|
270
|
+
; Type == "string"
|
|
271
|
+
-> field(Pattern, value, Value),
|
|
272
|
+
\+ string(Value),
|
|
273
|
+
Reason = "a string pattern must give its value"
|
|
274
|
+
; Reason = "the pattern's type must be regexp or string"
|
|
275
|
+
).
|
|
276
|
+
|
|
277
|
+
% implements REQ-kibi-source-proof-kinds
|
|
278
|
+
kind_problem(source, Certificate, Reason) :-
|
|
279
|
+
field(Certificate, source, Source),
|
|
280
|
+
field(Certificate, model, Model),
|
|
281
|
+
field(Model, members, Members),
|
|
282
|
+
( \+ ( is_dict(Source), get_dict(reader, Source, Reader), nonempty_string(Reader) )
|
|
283
|
+
-> Reason = "the certificate names no source reader"
|
|
284
|
+
; \+ is_dict(Model)
|
|
285
|
+
-> Reason = "the certificate has no model of what Kibi read"
|
|
286
|
+
; \+ sorted_distinct_strings(Members)
|
|
287
|
+
-> Reason = "the source model's members must be a sorted list of distinct strings"
|
|
288
|
+
; get_dict(entities, Model, Entities),
|
|
289
|
+
\+ ( is_list(Entities), maplist(source_entity_ok, Entities) )
|
|
290
|
+
-> Reason = "every source entity must have a name and columns of {name, nullable, optional}"
|
|
291
|
+
; get_dict(routes, Model, Routes),
|
|
292
|
+
\+ ( is_list(Routes), maplist(source_route_ok, Routes) )
|
|
293
|
+
-> Reason = "every source route must have a method, a path and a middleware list"
|
|
294
|
+
).
|
|
295
|
+
|
|
296
|
+
sorted_distinct_strings(List) :-
|
|
297
|
+
is_list(List),
|
|
298
|
+
forall(member(Item, List), string(Item)),
|
|
299
|
+
sort(List, Sorted),
|
|
300
|
+
Sorted == List.
|
|
301
|
+
|
|
302
|
+
source_entity_ok(Entity) :-
|
|
303
|
+
is_dict(Entity),
|
|
304
|
+
get_dict(name, Entity, Name), nonempty_string(Name),
|
|
305
|
+
get_dict(columns, Entity, Columns), is_list(Columns),
|
|
306
|
+
forall(member(Column, Columns),
|
|
307
|
+
( is_dict(Column),
|
|
308
|
+
get_dict(name, Column, ColumnName), nonempty_string(ColumnName),
|
|
309
|
+
get_dict(nullable, Column, Nullable), memberchk(Nullable, [true, false]),
|
|
310
|
+
get_dict(optional, Column, Optional), memberchk(Optional, [true, false]) )),
|
|
311
|
+
( get_dict(foreign_keys, Entity, Keys) -> is_list(Keys), forall(member(K, Keys), string(K)) ; true ).
|
|
312
|
+
|
|
313
|
+
source_route_ok(Route) :-
|
|
314
|
+
is_dict(Route),
|
|
315
|
+
get_dict(method, Route, Method), nonempty_string(Method),
|
|
316
|
+
get_dict(path, Route, Path), nonempty_string(Path),
|
|
317
|
+
get_dict(middleware, Route, Middleware), is_list(Middleware),
|
|
318
|
+
forall(member(M, Middleware), string(M)).
|
|
319
|
+
|
|
320
|
+
% table_problem(+Name, +Table, +Domains, -Reason): the first problem with
|
|
321
|
+
% one predicate table.
|
|
322
|
+
table_problem(Name, Table, Domains, Reason) :-
|
|
323
|
+
field(Table, symbol_id, Symbol),
|
|
324
|
+
field(Table, arg_types, Types),
|
|
325
|
+
field(Table, rows, Rows),
|
|
326
|
+
( \+ nonempty_string(Symbol)
|
|
327
|
+
-> format(string(Reason), "the table for ~w names no symbol_id", [Name])
|
|
328
|
+
; \+ (is_list(Types), Types \== [], length(Types, Arity), Arity =< 8,
|
|
329
|
+
forall(member(T, Types), (string(T), atom_string(TA, T), get_dict(TA, Domains, _))))
|
|
330
|
+
-> format(string(Reason), "the table for ~w must name 1 to 8 argument types, each with a domain", [Name])
|
|
331
|
+
; \+ is_list(Rows)
|
|
332
|
+
-> format(string(Reason), "the table for ~w has no rows", [Name])
|
|
333
|
+
; member(Row, Rows),
|
|
334
|
+
get_dict(error, Row, Error)
|
|
335
|
+
-> get_dict(args, Row, Args),
|
|
336
|
+
atomic_list_concat(Args, ', ', ArgText),
|
|
337
|
+
format(string(Reason), "~w(~w) failed: ~w", [Name, ArgText, Error])
|
|
338
|
+
; member(Row, Rows),
|
|
339
|
+
\+ table_row(Row, Types, Domains)
|
|
340
|
+
-> format(string(Reason), "every row of ~w must give one value per argument from its domain and a boolean", [Name])
|
|
341
|
+
; findall(Args, (member(Row, Rows), get_dict(args, Row, Args)), AllArgs),
|
|
342
|
+
msort(AllArgs, SortedArgs),
|
|
343
|
+
sort(AllArgs, UniqueArgs),
|
|
344
|
+
SortedArgs \== UniqueArgs
|
|
345
|
+
-> format(string(Reason), "the table for ~w lists a combination twice", [Name])
|
|
346
|
+
; length(Rows, Count),
|
|
347
|
+
foldl(domain_product(Domains), Types, 1, Expected),
|
|
348
|
+
Count =\= Expected
|
|
349
|
+
-> format(string(Reason), "the table for ~w has ~d rows, not one per combination of its domains (~d)", [Name, Count, Expected])
|
|
350
|
+
).
|
|
351
|
+
|
|
352
|
+
domain_product(Domains, Type, Count0, Count) :-
|
|
353
|
+
atom_string(TypeKey, Type),
|
|
354
|
+
get_dict(TypeKey, Domains, Domain),
|
|
355
|
+
get_dict(members, Domain, Members),
|
|
356
|
+
length(Members, N),
|
|
357
|
+
Count is Count0 * N.
|
|
358
|
+
|
|
359
|
+
table_row(Row, Types, Domains) :-
|
|
360
|
+
is_dict(Row),
|
|
361
|
+
get_dict(args, Row, Args),
|
|
362
|
+
is_list(Args),
|
|
363
|
+
same_length(Args, Types),
|
|
364
|
+
forall(nth1(I, Args, Arg),
|
|
365
|
+
( nth1(I, Types, T), atom_string(TA, T), get_dict(TA, Domains, D),
|
|
366
|
+
get_dict(members, D, Members), memberchk(Arg, Members) )),
|
|
367
|
+
get_dict(value, Row, Value),
|
|
368
|
+
memberchk(Value, [true, false]).
|
|
369
|
+
|
|
370
|
+
distinct_strings(Values) :-
|
|
371
|
+
is_list(Values),
|
|
372
|
+
Values \== [],
|
|
373
|
+
maplist(string, Values),
|
|
374
|
+
msort(Values, Sorted),
|
|
375
|
+
sort(Values, Unique),
|
|
376
|
+
Sorted == Unique.
|
|
377
|
+
|
|
188
378
|
% field(+Dict, +Key, -Value): the value, or '$missing' when absent.
|
|
189
379
|
field(Dict, Key, Value) :-
|
|
190
380
|
( is_dict(Dict), get_dict(Key, Dict, Value0)
|
|
@@ -206,6 +396,51 @@ arithmetic_constraint(X, Row, c(Op, X, Value)) :-
|
|
|
206
396
|
get_dict(value, Row, Value),
|
|
207
397
|
number(Value).
|
|
208
398
|
|
|
399
|
+
% "count = 4", for naming an assumption in a rejection.
|
|
400
|
+
constraint_text(Variable, Row, Text) :-
|
|
401
|
+
get_dict(operator, Row, OpString),
|
|
402
|
+
get_dict(value, Row, Value),
|
|
403
|
+
atom_string(Op, OpString),
|
|
404
|
+
operator_symbol(Op, Symbol),
|
|
405
|
+
format(atom(Text), "~w ~w ~w", [Variable, Symbol, Value]).
|
|
406
|
+
|
|
407
|
+
operator_symbol(eq, '=').
|
|
408
|
+
operator_symbol(neq, '!=').
|
|
409
|
+
operator_symbol(lt, '<').
|
|
410
|
+
operator_symbol(lte, '<=').
|
|
411
|
+
operator_symbol(gt, '>').
|
|
412
|
+
operator_symbol(gte, '>=').
|
|
413
|
+
|
|
414
|
+
% arithmetic_counterexample(+Type, +Assumptions, +Claim, -Value): a value
|
|
415
|
+
% next to one of the constants that satisfies every assumption and breaks the
|
|
416
|
+
% claim, so a rejection can name it (a count of 5 against "exactly 4").
|
|
417
|
+
arithmetic_counterexample(Type, Assumptions, Claim, Value) :-
|
|
418
|
+
findall(C, (member(Row, [Claim|Assumptions]), get_dict(value, Row, C)), Constants),
|
|
419
|
+
( Type == "int" -> Step = 1 ; Step = 0.5 ),
|
|
420
|
+
findall(V,
|
|
421
|
+
( member(C, [0|Constants]),
|
|
422
|
+
member(D, [0, -1, 1]),
|
|
423
|
+
V is C + D*Step,
|
|
424
|
+
( Type == "int" -> integer(V) ; true )
|
|
425
|
+
),
|
|
426
|
+
Candidates0),
|
|
427
|
+
sort(Candidates0, Candidates),
|
|
428
|
+
member(Value, Candidates),
|
|
429
|
+
forall(member(Row, Assumptions), concrete_holds(Row, Value)),
|
|
430
|
+
\+ concrete_holds(Claim, Value),
|
|
431
|
+
!.
|
|
432
|
+
|
|
433
|
+
concrete_holds(Row, X) :-
|
|
434
|
+
arithmetic_constraint(Y, Row, c(Op, Y, V)),
|
|
435
|
+
concrete_op(Op, X, V).
|
|
436
|
+
|
|
437
|
+
concrete_op(eq, X, V) :- X =:= V.
|
|
438
|
+
concrete_op(neq, X, V) :- X =\= V.
|
|
439
|
+
concrete_op(lt, X, V) :- X < V.
|
|
440
|
+
concrete_op(lte, X, V) :- X =< V.
|
|
441
|
+
concrete_op(gt, X, V) :- X > V.
|
|
442
|
+
concrete_op(gte, X, V) :- X >= V.
|
|
443
|
+
|
|
209
444
|
integer_variables("int", X, [X]).
|
|
210
445
|
integer_variables("real", _, []).
|
|
211
446
|
|
package/src/kb.pl
CHANGED
|
@@ -72,6 +72,7 @@
|
|
|
72
72
|
check_req_contradiction/1,
|
|
73
73
|
normalize_term_atom/2,
|
|
74
74
|
normalize_term_atom_list/2,
|
|
75
|
+
normalize_literal_atom_list/2,
|
|
75
76
|
changeset/4, % Export for testing
|
|
76
77
|
kb_uri/1
|
|
77
78
|
]).
|
|
@@ -85,7 +86,7 @@
|
|
|
85
86
|
:- use_module(library(sha), [sha_hash/3, hash_atom/2]).
|
|
86
87
|
:- use_module(library(http/json)).
|
|
87
88
|
:- use_module(library(aggregate), [aggregate_all/3]).
|
|
88
|
-
:- use_module(library(lists), [sum_list/2]).
|
|
89
|
+
:- use_module(library(lists), [sum_list/2, subtract/3, intersection/3]).
|
|
89
90
|
:- use_module(library(ordsets)).
|
|
90
91
|
:- use_module(library(solution_sequences), [distinct/1, limit/2, offset/2, order_by/2]).
|
|
91
92
|
:- use_module('../schema/entities.pl', [entity_type/1, entity_property/3, required_property/2]).
|
|
@@ -3080,6 +3081,23 @@ normalize_term_atom_list(Raw, Atoms) :-
|
|
|
3080
3081
|
!,
|
|
3081
3082
|
maplist(normalize_term_atom, Value, Atoms).
|
|
3082
3083
|
|
|
3084
|
+
%% normalize_literal_atom_list(+Raw, -Atoms)
|
|
3085
|
+
% The members of a stored list as atoms, verbatim. Unlike
|
|
3086
|
+
% normalize_term_atom_list it keeps a member's '/' segments, so set members
|
|
3087
|
+
% such as rxjs/operators, GET /api/users or finance/de stay whole.
|
|
3088
|
+
normalize_literal_atom_list(Raw, Atoms) :-
|
|
3089
|
+
unwrap_rdf_value(Raw, Value),
|
|
3090
|
+
is_list(Value),
|
|
3091
|
+
maplist(normalize_literal_atom, Value, Atoms).
|
|
3092
|
+
|
|
3093
|
+
normalize_literal_atom(Raw, Atom) :-
|
|
3094
|
+
unwrap_rdf_value(Raw, Value),
|
|
3095
|
+
( atom(Value) -> Atom = Value
|
|
3096
|
+
; string(Value) -> atom_string(Atom, Value)
|
|
3097
|
+
; number(Value) -> atom_number(Atom, Value)
|
|
3098
|
+
; term_string(Value, String), atom_string(Atom, String)
|
|
3099
|
+
).
|
|
3100
|
+
|
|
3083
3101
|
normalize_term_integer(Raw, Integer) :-
|
|
3084
3102
|
unwrap_rdf_value(Raw, Value),
|
|
3085
3103
|
( integer(Value)
|
|
@@ -3125,7 +3143,7 @@ value_from_props(Props, bool, Value) :- memberchk(value_bool=ValueRaw, Props), !
|
|
|
3125
3143
|
% in any order compare equal.
|
|
3126
3144
|
value_from_props(Props, set, Value) :-
|
|
3127
3145
|
memberchk(value_set=ValueRaw, Props),
|
|
3128
|
-
|
|
3146
|
+
normalize_literal_atom_list(ValueRaw, Atoms),
|
|
3129
3147
|
!,
|
|
3130
3148
|
sort(Atoms, Value).
|
|
3131
3149
|
value_from_props(_, _, '').
|
|
@@ -3226,6 +3244,35 @@ same_value(TypeA, ValA, TypeB, ValB) :-
|
|
|
3226
3244
|
values_conflict(eq, ValA, eq, ValB, Type) :-
|
|
3227
3245
|
\+ same_value(Type, ValA, Type, ValB).
|
|
3228
3246
|
|
|
3247
|
+
% A set fact with includes says every member of its set has the property;
|
|
3248
|
+
% eq on a set says exactly those members do. Two includes facts always agree
|
|
3249
|
+
% (the members having the property include both sets); includes conflicts
|
|
3250
|
+
% with eq only when it names a member the eq set leaves out.
|
|
3251
|
+
% implements REQ-kibi-non-numeric-proof-kinds
|
|
3252
|
+
values_conflict(includes, Included, eq, Exact, set) :-
|
|
3253
|
+
\+ subtract(Included, Exact, []).
|
|
3254
|
+
values_conflict(eq, Exact, includes, Included, set) :-
|
|
3255
|
+
\+ subtract(Included, Exact, []).
|
|
3256
|
+
|
|
3257
|
+
% subset_of says every member with the property is in its set, excludes that
|
|
3258
|
+
% no member of its set has it. subset_of conflicts with a set that must have
|
|
3259
|
+
% the property (eq or includes) when that set names a member outside it;
|
|
3260
|
+
% excludes conflicts when the two sets share a member. subset_of and excludes
|
|
3261
|
+
% never conflict with each other or themselves: the empty set satisfies them.
|
|
3262
|
+
% implements REQ-kibi-source-proof-kinds
|
|
3263
|
+
values_conflict(subset_of, Allowed, Op, Required, set) :-
|
|
3264
|
+
memberchk(Op, [eq, includes]),
|
|
3265
|
+
\+ subtract(Required, Allowed, []).
|
|
3266
|
+
values_conflict(Op, Required, subset_of, Allowed, set) :-
|
|
3267
|
+
memberchk(Op, [eq, includes]),
|
|
3268
|
+
\+ subtract(Required, Allowed, []).
|
|
3269
|
+
values_conflict(excludes, Excluded, Op, Required, set) :-
|
|
3270
|
+
memberchk(Op, [eq, includes]),
|
|
3271
|
+
intersection(Excluded, Required, [_|_]).
|
|
3272
|
+
values_conflict(Op, Required, excludes, Excluded, set) :-
|
|
3273
|
+
memberchk(Op, [eq, includes]),
|
|
3274
|
+
intersection(Excluded, Required, [_|_]).
|
|
3275
|
+
|
|
3229
3276
|
% Eq/neq conflict on the same scalar value.
|
|
3230
3277
|
values_conflict(eq, ValA, neq, ValB, Type) :-
|
|
3231
3278
|
same_value(Type, ValA, Type, ValB).
|