001package gudusoft.gsqlparser.lineage.v2.fold;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.List;
006import java.util.Map;
007import java.util.Set;
008import java.util.SortedSet;
009import java.util.TreeSet;
010
011/**
012 * What FOLD-V1 yields for one statement ({@code algorithms/fold-v1.md}, Section 2, item 1): the Fact
013 * Occurrences, the FactCandidates, and the requirements on every write of a field. References are the
014 * document-local {@code ref}s of the document the statement was folded from.
015 *
016 * @since 4.2.17
017 */
018public final class StatementFold {
019
020    private final long statementRef;
021    private final List<FactOccurrence> facts;
022    private final List<Candidate> candidates;
023    private final List<WriteRequirement> writes;
024
025    StatementFold(long statementRef, List<FactOccurrence> facts, List<Candidate> candidates,
026                  List<WriteRequirement> writes) {
027        this.statementRef = statementRef;
028        this.facts = Collections.unmodifiableList(new ArrayList<FactOccurrence>(facts));
029        this.candidates = Collections.unmodifiableList(new ArrayList<Candidate>(candidates));
030        this.writes = Collections.unmodifiableList(new ArrayList<WriteRequirement>(writes));
031    }
032
033    public long getStatementRef() {
034        return statementRef;
035    }
036
037    /** One entry per Fact Occurrence (Section 8.1), ordered by source, target, label, path and ordinal. */
038    public List<FactOccurrence> getFacts() {
039        return facts;
040    }
041
042    /** One entry per FactCandidate (Section 7.1, items 4 to 6). */
043    public List<Candidate> getCandidates() {
044        return candidates;
045    }
046
047    /** One entry per field the statement writes (Sections 7.4 and 7.5), ordered by target. */
048    public List<WriteRequirement> getWrites() {
049        return writes;
050    }
051
052    static <T> List<T> frozen(List<T> list) {
053        return Collections.unmodifiableList(new ArrayList<T>(list));
054    }
055
056    static List<List<Long>> frozenWalks(List<List<Long>> walks) {
057        List<List<Long>> out = new ArrayList<List<Long>>();
058        for (List<Long> walk : walks) {
059            out.add(Collections.unmodifiableList(new ArrayList<Long>(walk)));
060        }
061        return Collections.unmodifiableList(out);
062    }
063
064    /** A detached, frozen copy of a binding, so that no result aliases the document it was folded from. */
065    static Map<?, ?> freeze(Map<?, ?> binding) {
066        return binding == null ? null : (Map<?, ?>) gudusoft.gsqlparser.lineage.v2.json.CanonicalJson.immutableCopy(binding);
067    }
068
069    static SortedSet<Long> frozen(Set<Long> set) {
070        return Collections.unmodifiableSortedSet(new TreeSet<Long>(set));
071    }
072
073    /** One (source, target) pair of an outcome (Section 7.1, item 5). */
074    public static final class Pair {
075        private final Object source;
076        private final Object target;
077
078        public Pair(Object source, Object target) {
079            this.source = source;
080            this.target = target;
081        }
082
083        /** The source ref; for the candidate set of an {@code UNRESOLVED} reference, that set's ref. */
084        public Object getSource() {
085            return source;
086        }
087
088        public Object getTarget() {
089            return target;
090        }
091
092        @Override
093        public boolean equals(Object other) {
094            if (!(other instanceof Pair)) {
095                return false;
096            }
097            Pair pair = (Pair) other;
098            return FoldKernel.same(source, pair.source) && FoldKernel.same(target, pair.target);
099        }
100
101        @Override
102        public int hashCode() {
103            return FoldKernel.hash(source) * 31 + FoldKernel.hash(target);
104        }
105
106        @Override
107        public String toString() {
108            return "(" + source + ", " + target + ")";
109        }
110    }
111
112    /**
113     * One end of a FactCandidate: a fixed endpoint, or a candidate set with its membership, and, when another reference
114     * decides it (fold-v1 Section 7.1, item 5), the decision.
115     */
116    public static final class End {
117        private final Object endpointRef;
118        private final List<Object> members;
119        private final Object membership;
120        private final Decision decision;
121
122        private End(Object endpointRef, List<Object> members, Object membership, Decision decision) {
123            this.endpointRef = endpointRef;
124            this.members = members;
125            this.membership = membership;
126            this.decision = decision;
127        }
128
129        static End fixed(Object endpointRef) {
130            return new End(endpointRef, null, null, null);
131        }
132
133        static End set(List<Object> members, Object membership) {
134            return set(members, membership, null);
135        }
136
137        static End set(List<Object> members, Object membership, Decision decision) {
138            return new End(null, Collections.unmodifiableList(new ArrayList<Object>(members)), membership, decision);
139        }
140
141        /** How another reference decides this candidate-set end; null when none does (or for a fixed end). */
142        public Decision getDecision() {
143            return decision;
144        }
145
146        public boolean isFixed() {
147            return members == null;
148        }
149
150        /** The endpoint of a fixed end; null for a candidate set. */
151        public Object getEndpointRef() {
152            return endpointRef;
153        }
154
155        /** The members of a candidate-set end, sorted; null for a fixed end. */
156        public List<Object> getMembers() {
157            return members;
158        }
159
160        /** {@code COMPLETE} or {@code INCOMPLETE} for a candidate-set end; null for a fixed end. */
161        public Object getMembership() {
162            return membership;
163        }
164
165        @Override
166        public boolean equals(Object other) {
167            if (!(other instanceof End)) {
168                return false;
169            }
170            End end = (End) other;
171            if (isFixed() != end.isFixed()) {
172                return false;
173            }
174            if (isFixed()) {
175                return FoldKernel.same(endpointRef, end.endpointRef);
176            }
177            if (members.size() != end.members.size() || !FoldKernel.same(membership, end.membership)
178                    || (decision == null ? end.decision != null : !decision.equals(end.decision))) {
179                return false;
180            }
181            for (int i = 0; i < members.size(); i++) {
182                if (!FoldKernel.same(members.get(i), end.members.get(i))) {
183                    return false;
184                }
185            }
186            return true;
187        }
188
189        @Override
190        public int hashCode() {
191            if (isFixed()) {
192                return FoldKernel.hash(endpointRef);
193            }
194            int h = FoldKernel.hash(membership);
195            for (Object member : members) {
196                h = h * 31 + FoldKernel.hash(member);
197            }
198            return h;
199        }
200
201        @Override
202        public String toString() {
203            return isFixed() ? String.valueOf(endpointRef) : "SET " + membership + " " + members
204                    + (decision == null ? "" : " " + decision);
205        }
206    }
207
208    /**
209     * How a reference decides a FactCandidate's candidate-set end (fold-v1 Section 7.1, item 5): the deciding
210     * reference's candidate set (in the ExplainGraph; at a published end, the document's set with the same members),
211     * and the pairs {alternative, member}, sorted. Two decisions are equal when the deciding sets have the same members
212     * and membership and the pairs are equal.
213     */
214    public static final class Decision {
215        private final Object decidingRef;
216        private final List<Object> alternatives;
217        private final Object membership;
218        private final List<Pair> alignment;
219
220        Decision(Object decidingRef, List<Object> alternatives, Object membership, List<Pair> alignment) {
221            this.decidingRef = decidingRef;
222            List<Object> sorted = new ArrayList<Object>(alternatives);
223            Collections.sort(sorted, FoldKernel.REF_ORDER);
224            this.alternatives = Collections.unmodifiableList(sorted);
225            this.membership = membership;
226            List<Pair> pairs = new ArrayList<Pair>(alignment);
227            Collections.sort(pairs, new java.util.Comparator<Pair>() {
228                @Override
229                public int compare(Pair a, Pair b) {
230                    int c = FoldKernel.REF_ORDER.compare(a.getSource(), b.getSource());
231                    return c != 0 ? c : FoldKernel.REF_ORDER.compare(a.getTarget(), b.getTarget());
232                }
233            });
234            this.alignment = Collections.unmodifiableList(pairs);
235        }
236
237        /** The deciding reference's candidate set. */
238        public Object getDecidingRef() {
239            return decidingRef;
240        }
241
242        /** The members of the deciding set, sorted. */
243        public List<Object> getAlternatives() {
244            return alternatives;
245        }
246
247        public Object getMembership() {
248            return membership;
249        }
250
251        /** The pairs: source is the alternative, target the member of the end. */
252        public List<Pair> getAlignment() {
253            return alignment;
254        }
255
256        @Override
257        public boolean equals(Object other) {
258            if (!(other instanceof Decision)) {
259                return false;
260            }
261            Decision d = (Decision) other;
262            return alternatives.equals(d.alternatives) && FoldKernel.same(membership, d.membership)
263                    && alignment.equals(d.alignment);
264        }
265
266        @Override
267        public int hashCode() {
268            return alternatives.hashCode() * 31 + alignment.hashCode();
269        }
270
271        @Override
272        public String toString() {
273            return "DECIDED " + alternatives + " " + membership + " " + alignment;
274        }
275    }
276
277    /** A Fact Occurrence (Section 8.1): the proposition, its path and ordinal, proof, bindings and support. */
278    public static final class FactOccurrence {
279        private final Object sourceRef;
280        private final Object targetRef;
281        private final FoldLabel label;
282        private final Object path;
283        private final Long offset;
284        private final int ordinal;
285        private final String proofStatus;
286        private final SortedSet<Long> support;
287        private final Map<?, ?> sourceBinding;
288        private final Map<?, ?> targetBinding;
289
290        private final List<List<Long>> supportWalks;
291
292        FactOccurrence(Object sourceRef, Object targetRef, FoldLabel label, Object path, Long offset, int ordinal,
293                       String proofStatus, Set<Long> support, Map<?, ?> sourceBinding, Map<?, ?> targetBinding,
294                       List<List<Long>> supportWalks) {
295            this.supportWalks = frozenWalks(supportWalks);
296            this.sourceRef = sourceRef;
297            this.targetRef = targetRef;
298            this.label = label;
299            this.path = path;
300            this.offset = offset;
301            this.ordinal = ordinal;
302            this.proofStatus = proofStatus;
303            this.support = frozen(support);
304            this.sourceBinding = freeze(sourceBinding);
305            this.targetBinding = freeze(targetBinding);
306        }
307
308        public Object getSourceRef() {
309            return sourceRef;
310        }
311
312        public Object getTargetRef() {
313            return targetRef;
314        }
315
316        public FoldLabel getLabel() {
317            return label;
318        }
319
320        /** The semantic path of the reference read by the first edge ({@code referencePath}), or null. */
321        public Object getPath() {
322            return path;
323        }
324
325        /** The UTF-8 offset of that reference ({@code referenceStart}), or null. */
326        public Long getOffset() {
327            return offset;
328        }
329
330        /** The rank among the Occurrences of the same Fact and path, by the position of the reference. */
331        public int getOrdinal() {
332            return ordinal;
333        }
334
335        /** {@code PROVEN} or {@code ASSUMED} (Section 7.2, item 2). */
336        public String getProofStatus() {
337            return proofStatus;
338        }
339
340        /** The micro-edges of the support subgraph (Section 8.2, item 1). */
341        public SortedSet<Long> getSupport() {
342            return support;
343        }
344
345        /**
346         * Fold walks (micro-edge refs in walk order) that each yield this Occurrence and together cover its support
347         * subgraph: the {@code supportPaths} to publish (Section 8.2, items 3 and 4).
348         */
349        public List<List<Long>> getSupportWalks() {
350            return supportWalks;
351        }
352
353        /**
354         * The source-side binding of the first edge (Section 8.1, item 2), or null when it has none. When several
355         * walks support the Occurrence, the binding comes from a walk that decides its proof (an unconditional
356         * {@code PROVEN} walk for a {@code PROVEN} Occurrence), and among those from the first edge earliest in
357         * the text.
358         */
359        public Map<?, ?> getSourceBinding() {
360            return sourceBinding;
361        }
362
363        /** The target-side binding of the last edge, chosen like {@link #getSourceBinding()}. */
364        public Map<?, ?> getTargetBinding() {
365            return targetBinding;
366        }
367
368        @Override
369        public String toString() {
370            return sourceRef + " -" + label + "-> " + targetRef + " @" + path + "#" + ordinal + " " + proofStatus;
371        }
372    }
373
374    /**
375     * The binding of one side of a FactCandidate Occurrence (Section 7.1, item 6). When
376     * {@link #namesOwnCandidateSet()} is true, the binding's {@code candidateSetRef} stands for the candidate set
377     * of that side of the FactCandidate itself, whose ref the producer assigns.
378     */
379    public static final class CandidateBinding {
380        private final Map<?, ?> binding;
381        private final boolean ownCandidateSet;
382
383        CandidateBinding(Map<?, ?> binding, boolean ownCandidateSet) {
384            this.binding = freeze(binding);
385            this.ownCandidateSet = ownCandidateSet;
386        }
387
388        /** The binding as found on the micro-edge, or null when the edge has none. */
389        public Map<?, ?> getBinding() {
390            return binding;
391        }
392
393        public boolean namesOwnCandidateSet() {
394            return ownCandidateSet;
395        }
396
397        @Override
398        public String toString() {
399            return binding + (ownCandidateSet ? " (own candidate set)" : "");
400        }
401    }
402
403    /** A FactCandidate (Section 7.1, items 4 to 6) with its Occurrence. */
404    public static final class Candidate {
405        private final FoldLabel label;
406        private final List<Object> references;
407        private final End source;
408        private final End target;
409        private final Set<Set<Pair>> outcomes;
410        private final Set<Set<Pair>> effectiveOutcomes;
411        private final String proofStatus;
412        private final SortedSet<Long> support;
413        private final Object path;
414        private final Long offset;
415        int ordinal;
416        CandidateBinding sourceBinding;
417        CandidateBinding targetBinding;
418
419        private final List<List<Long>> supportWalks;
420
421        Candidate(FoldLabel label, List<Object> references, End source, End target, Set<Set<Pair>> outcomes,
422                  Set<Set<Pair>> effectiveOutcomes, String proofStatus, Set<Long> support, Object path, Long offset,
423                  List<List<Long>> supportWalks) {
424            this.supportWalks = frozenWalks(supportWalks);
425            this.label = label;
426            this.references = frozen(references);
427            this.source = source;
428            this.target = target;
429            this.outcomes = outcomes == null ? null : FoldKernel.frozenOutcomes(outcomes);
430            this.effectiveOutcomes = FoldKernel.frozenOutcomes(effectiveOutcomes);
431            this.proofStatus = proofStatus;
432            this.support = frozen(support);
433            this.path = path;
434            this.offset = offset;
435        }
436
437        public FoldLabel getLabel() {
438            return label;
439        }
440
441        /** The ambiguous references of the group, named by their candidate sets, sorted. */
442        public List<Object> getReferences() {
443            return references;
444        }
445
446        public End getSource() {
447            return source;
448        }
449
450        public End getTarget() {
451            return target;
452        }
453
454        /** The outcomes to list in {@code outcomes}; null when they are the implied ones. */
455        public Set<Set<Pair>> getOutcomes() {
456            return outcomes;
457        }
458
459        /** The outcomes that hold: the listed ones, or the implied ones when none are listed. */
460        public Set<Set<Pair>> getEffectiveOutcomes() {
461            return effectiveOutcomes;
462        }
463
464        public String getProofStatus() {
465            return proofStatus;
466        }
467
468        public SortedSet<Long> getSupport() {
469            return support;
470        }
471
472        /** Fold walks that each yield this FactCandidate and together cover its support subgraph. */
473        public List<List<Long>> getSupportWalks() {
474            return supportWalks;
475        }
476
477        /** The semantic path of the deciding reference, or null. */
478        public Object getPath() {
479            return path;
480        }
481
482        /** The UTF-8 offset of the deciding reference, or null. */
483        public Long getOffset() {
484            return offset;
485        }
486
487        /** The rank among the FactCandidates of the statement with the same subject and path (identity-v1 5.4). */
488        public int getOrdinal() {
489            return ordinal;
490        }
491
492        public CandidateBinding getSourceBinding() {
493            return sourceBinding;
494        }
495
496        public CandidateBinding getTargetBinding() {
497            return targetBinding;
498        }
499
500        @Override
501        public String toString() {
502            return source + " -" + label + "-> " + target + " @" + path + "#" + ordinal + " " + proofStatus
503                    + (outcomes == null ? "" : " outcomes " + outcomes);
504        }
505    }
506
507    /**
508     * A terminal input of a write (Section 7.5): its kind, disclosure, the literal or hash token, and a parameter's
509     * marker as written with, for an anonymous marker, its position (two parameters of one write are two inputs).
510     */
511    public static final class TerminalInput {
512        private final Object kind;
513        private final Object disclosure;
514        private final Object value;
515        private final Object parameterText;
516        private final Object parameterOrdinal;
517
518        TerminalInput(Object kind, Object disclosure, Object value, Object parameterText, Object parameterOrdinal) {
519            this.kind = kind;
520            this.disclosure = disclosure;
521            this.value = value;
522            this.parameterText = parameterText;
523            this.parameterOrdinal = parameterOrdinal;
524        }
525
526        /** A PARAMETER's marker or name as written, else null. */
527        public Object getParameterText() {
528            return parameterText;
529        }
530
531        /** An anonymous PARAMETER marker's position among the statement's anonymous markers, else null. */
532        public Object getParameterOrdinal() {
533            return parameterOrdinal;
534        }
535
536        public Object getKind() {
537            return kind;
538        }
539
540        public Object getDisclosure() {
541            return disclosure;
542        }
543
544        /** The literal of a {@code PRESENT} literal, the canonical JSON of a {@code HASHED} token, else null. */
545        public Object getValue() {
546            return value;
547        }
548
549        @Override
550        public boolean equals(Object other) {
551            if (!(other instanceof TerminalInput)) {
552                return false;
553            }
554            TerminalInput input = (TerminalInput) other;
555            return FoldKernel.same(kind, input.kind) && FoldKernel.same(disclosure, input.disclosure)
556                    && FoldKernel.same(value, input.value) && FoldKernel.same(parameterText, input.parameterText)
557                    && FoldKernel.same(parameterOrdinal, input.parameterOrdinal);
558        }
559
560        @Override
561        public int hashCode() {
562            return (((FoldKernel.hash(kind) * 31 + FoldKernel.hash(disclosure)) * 31 + FoldKernel.hash(value)) * 31
563                    + FoldKernel.hash(parameterText)) * 31 + FoldKernel.hash(parameterOrdinal);
564        }
565
566        @Override
567        public String toString() {
568            return kind + " " + disclosure + " " + value + (parameterText == null ? "" : " " + parameterText)
569                    + (parameterOrdinal == null ? "" : " #" + parameterOrdinal);
570        }
571    }
572
573    /** What a write of one field must carry (Sections 7.4 and 7.5). */
574    public static final class WriteRequirement {
575        private final Object targetRef;
576        private final String bound;
577        private final List<FactOccurrence> facts;
578        private final List<Candidate> candidates;
579        private final Set<TerminalInput> terminals;
580        private final SortedSet<Long> diagnosticRefs;
581
582        WriteRequirement(Object targetRef, String bound, List<FactOccurrence> facts, List<Candidate> candidates,
583                         Set<TerminalInput> terminals, Set<Long> diagnosticRefs) {
584            this.targetRef = targetRef;
585            this.bound = bound;
586            this.facts = frozen(facts);
587            this.candidates = frozen(candidates);
588            this.terminals = Collections.unmodifiableSet(new java.util.LinkedHashSet<TerminalInput>(terminals));
589            this.diagnosticRefs = frozen(diagnosticRefs);
590        }
591
592        public Object getTargetRef() {
593            return targetRef;
594        }
595
596        /** The highest {@code upstreamCompleteness} the write may declare: COMPLETE, PARTIAL or UNKNOWN. */
597        public String getBound() {
598            return bound;
599        }
600
601        /** One Occurrence of each Fact into the field; each Fact is a {@code FIELD_REF} input. */
602        public List<FactOccurrence> getFacts() {
603            return facts;
604        }
605
606        /** The FactCandidates whose target end is the field or contains it; each is a {@code FIELD_REF} input. */
607        public List<Candidate> getCandidates() {
608            return candidates;
609        }
610
611        /** The terminals that reach the field through {@code VALUE} micro-edges only. */
612        public Set<TerminalInput> getTerminals() {
613            return terminals;
614        }
615
616        /** The diagnostics of the nodes and holes that lowered the bound; the write must link them. */
617        public SortedSet<Long> getDiagnosticRefs() {
618            return diagnosticRefs;
619        }
620
621        @Override
622        public String toString() {
623            return targetRef + " " + bound + " facts " + facts.size() + " candidates " + candidates.size()
624                    + " terminals " + terminals + " diagnostics " + diagnosticRefs;
625        }
626    }
627}