Refactoring: extract Justified interface and hide lower-level work with Int sets behind it

Make Occurrence a Justified entity and drop .justifications() from
ConstraintOccurrence.java because justifications are used only internally.
A single justification is now called Evidence (typealias to Int),
so Justified entities are justified by Evidence-s.
This commit is contained in:
Grigorii Kirgizov 2020-02-28 14:05:05 +03:00
parent 3193a9c078
commit 37dd3ee4a8
13 changed files with 157 additions and 99 deletions

View File

@ -0,0 +1,74 @@
/*
* Copyright 2014-2020 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.mps.logic.reactor.core
import gnu.trove.set.TIntSet
import gnu.trove.set.hash.TIntHashSet
/**
* An identifier whose presence provides evidence for some fact.
*/
typealias Evidence = Int
/**
* [Justifications] is a set of [Evidence]s.
*/
typealias Justifications = TIntSet
fun emptyJustifications(): Justifications = TIntHashSet(1)
fun justsOf(vararg elements: Evidence): Justifications = TIntHashSet(elements)
fun justsFromCollection(collection: Collection<Evidence>): Justifications = TIntHashSet(collection)
fun justsCopy(other: Justifications): Justifications = TIntHashSet(other)
/**
* A logical entity whose existence is supported by a number of some facts (or premises, or evidences).
* Hence it is said that its existence is justified by them.
* In its turn can serve as an evidence for other justified entities.
*/
interface Justified {
/**
* An identifier of this [Justified] entity.
* Can serve as evidence for other [Justified] entities.
*/
val evidence: Evidence
/**
* Premises (or facts) supporting existence of this [Justified] entity.
*/
fun justifications(): Justifications
/**
* Checks whether this [Justified] entity is supported by [other] [Justified] entity.
*/
fun justifiedBy(other: Justified): Boolean =
this.justifications().contains(other.evidence)
/**
* Checks whether this [Justified] entity is supported any of the [others] [Justified] entities.
*/
fun justifiedByAny(others: Collection<Justified>): Boolean =
others.any { this.justifiedBy(it) }
}
interface EvidenceSource {
fun nextEvidence(): Evidence
}

View File

@ -16,8 +16,6 @@
package jetbrains.mps.logic.reactor.core
import gnu.trove.set.TIntSet
import gnu.trove.set.hash.TIntHashSet
import jetbrains.mps.logic.reactor.core.internal.FeedbackStatus
import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
@ -26,16 +24,6 @@ import jetbrains.mps.logic.reactor.logical.LogicalContext
import jetbrains.mps.logic.reactor.program.Constraint
typealias Justs = TIntSet
//fun emptyJusts() = object : TIntSet {}
fun emptyJusts() = TIntHashSet(1)
fun justsOf(vararg elements: Int) = TIntHashSet(elements)
fun justsFromCollection(collection: Collection<Int>) = TIntHashSet(collection)
fun justsCopy(other: Justs) = TIntHashSet(other)
fun Justs.intersects(other: Iterable<Int>): Boolean = other.any { this.contains(it) }
/**
* Class representing a single constraint occurrence.
*
@ -45,9 +33,10 @@ class Occurrence (observable: LogicalStateObservable,
val constraint: Constraint,
val logicalContext: LogicalContext,
val arguments: List<*>,
val justifications: Justs,
override val evidence: Evidence,
private val justifications: Justifications,
val ruleUniqueTag: Any? = null):
ConstraintOccurrence, ForwardingLogicalObserver
ConstraintOccurrence, ForwardingLogicalObserver, Justified
{
var alive = true
@ -57,6 +46,7 @@ class Occurrence (observable: LogicalStateObservable,
val identity = System.identityHashCode(this)
init {
justifications.add(evidence) // ensure reflexivity of justifications
revive(observable)
}
@ -68,7 +58,7 @@ class Occurrence (observable: LogicalStateObservable,
override fun ruleUniqueTag(): Any? = ruleUniqueTag
override fun justifications(): Justs = justifications
override fun justifications(): Justifications = justifications
override fun valueUpdated(logical: Logical<*>, controller: Controller) = doReactivate(controller)
@ -111,7 +101,8 @@ class Occurrence (observable: LogicalStateObservable,
fun Constraint.occurrence(observable: LogicalStateObservable,
arguments: List<*>,
justifications: Justs,
evidence: Evidence,
justifications: Justifications,
logicalContext: LogicalContext,
ruleUniqueTag: Any? = null): Occurrence =
Occurrence(observable, this, logicalContext, arguments, justifications, ruleUniqueTag)
Occurrence(observable, this, logicalContext, arguments, evidence, justifications, ruleUniqueTag)

View File

@ -79,7 +79,7 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di
* - pruning invalidated occurrences and matches from Dispatcher's state
*/
fun invalidateRuleMatches(ruleIds: Set<Any>): Set<Any> {
val justificationRoots = mutableListOf<Int>()
val justificationRoots = mutableListOf<Justified>()
val allInvalidatedIds = mutableSetOf<Any>()
val it = this.iterator()
@ -89,11 +89,11 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di
val chunk = it.next()
if (chunk is MatchJournal.MatchChunk && ruleIds.contains(chunk.ruleUniqueTag)) {
justificationRoots.add(chunk.id)
justificationRoots.add(chunk)
}
// Invalidating dependent chunks
if (chunk.justifications.intersects(justificationRoots)) {
if (chunk.justifiedByAny(justificationRoots)) {
// Remove chunk from the journal
it.remove()
@ -120,7 +120,7 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di
// by definition of Chunk and principal rule, all occurrences from the head are principal
val matchedOccs = chunk.match.allHeads() as Iterable<Occurrence>
val validOccs = matchedOccs.filter { occ ->
!occ.justifications().intersects(justificationRoots)
!occ.justifiedByAny(justificationRoots)
}
// assert(matchedOccs.all { it.isPrincipal() })
@ -234,7 +234,6 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di
val matches = dispatchingFront.matches().toList()
val currentMatches =
// todo: assert that there can be no future matches on non-principal occurrences?
if (isFront() || !active.isPrincipal()) {
matches
} else {

View File

@ -42,7 +42,7 @@ internal class ControllerImpl (
/** For tests only */
override fun evaluate(occ: Occurrence): StoreView {
// create the internal occurrence
val active = occ.constraint().occurrence(logicalStateObservable(), occ.arguments(), occ.justifications(), object: LogicalContext {
val active = occ.constraint().occurrence(logicalStateObservable(), occ.arguments(), occ.evidence, occ.justifications(), object: LogicalContext {
override fun <V : Any> variable(metaLogical: MetaLogical<V>): Logical<V>? = null
})
@ -77,9 +77,7 @@ internal class ControllerImpl (
// FIXME noLogicalContext
val context = Context(NORMAL(), noLogicalContext, null, trace)
// fixme: is it valid to always provide current justifications?
// while this method is used only in one place at program kick-off, yes, it's initial justs provided.
activateConstraint(constraint, processing.justs(), context)
activateConstraint(constraint, processing.justifications(), context)
return context.currentStatus()
}
@ -156,13 +154,13 @@ internal class ControllerImpl (
val savedPos = processing.currentPos()
val currentJusts = processing.justs()
val currentJusts = processing.justifications()
for (item in body) {
val itemOk = when (item) {
is Constraint -> {
// track justifications only for principal constraints
val justs = if (ispec.isPrincipal(item)) currentJusts else emptyJusts()
val justs = if (ispec.isPrincipal(item)) currentJusts else emptyJustifications()
activateConstraint(item, justs, context)
}
is Predicate -> tellPredicate(item, context)
@ -220,13 +218,13 @@ internal class ControllerImpl (
return context.currentStatus()
}
private fun activateConstraint(constraint: Constraint, justs: Justs, context: Context) : Boolean {
private fun activateConstraint(constraint: Constraint, justifications: Justifications, context: Context) : Boolean {
val args = supervisor.instantiateArguments(constraint.arguments(), context.logicalContext, context)
return context.eval { status ->
profiler.profile<FeedbackStatus>("activate_${constraint.symbol()}") {
constraint.occurrence(logicalStateObservable(), args, justsCopy(justs), context.logicalContext, context.ruleUniqueTag).let { occ ->
constraint.occurrence(logicalStateObservable(), args, processing.nextEvidence(), justsCopy(justifications), context.logicalContext, context.ruleUniqueTag).let { occ ->
trace.activate(occ)
processing.processActivated(this, occ, status)
}

View File

@ -132,7 +132,7 @@ internal class ExecutionQueue(
val placeToInsertFound = beforeChunk is MatchJournal.MatchChunk
&& ruleOrdering.isEarlierThan(candidateRule, beforeChunk.match.rule())
val childChunksEnded = !beforeChunk.isDescendantOf(parentChunk.id)
val childChunksEnded = !beforeChunk.isDescendantOf(parentChunk)
return (childChunksEnded || placeToInsertFound)
}

View File

@ -23,7 +23,7 @@ import jetbrains.mps.logic.reactor.util.Id
import java.util.*
interface MatchJournal : MutableIterable<MatchJournal.Chunk> {
interface MatchJournal : MutableIterable<MatchJournal.Chunk>, EvidenceSource {
/**
* Add new [Chunk] for matches of principal rules.
@ -133,17 +133,15 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk> {
/**
* Immutable snapshot of [MatchJournal].
*/
data class View(private val chunks: List<Chunk>, private val nextChunkId: Int) : MatchJournalView {
data class View(private val chunks: List<Chunk>, private val evidenceSeed: Evidence) : MatchJournalView {
override fun getChunks(): List<Chunk> = chunks
override fun getNextChunkId(): Int = nextChunkId
override fun getEvidenceSeed(): Evidence = evidenceSeed
override fun getStoreView(): StoreView = StoreViewImpl(
chunks.flatMap { it.entriesLog() }.allOccurrences().asSequence()
)
}
interface Chunk : MatchJournalChunk {
val id: Int
val justifications: Justs
interface Chunk : MatchJournalChunk, Justified {
// fixme: hide rm-mutability
var entries: MutableList<Entry>
@ -155,8 +153,8 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk> {
override fun toString() = (if (discarded) '-' else '+') + occ.toString()
}
fun isDescendantOf(chunkId: Int): Boolean = justifications.contains(chunkId)
fun isTopLevel(): Boolean = justifications.size() <= 1 // this condition implies that there're no ancestor chunks
fun isDescendantOf(chunk: Chunk): Boolean = this.justifiedBy(chunk)
fun isTopLevel(): Boolean = justifications().size() <= 1 // this condition implies that there're no ancestor chunks
fun activatedLog(): List<Occurrence> = entriesLog().filter { !it.discarded() }.map { it.occ() }
fun discardedLog(): List<Occurrence> = entriesLog().filter { it.discarded() }.map { it.occ() }
@ -170,27 +168,25 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk> {
fun toPos(): Pos = Pos(this, entriesLog().size)
}
class MatchChunk(override val id: Int, val match: RuleMatch) : Chunk {
override val justifications: Justs = match.headJustifications().apply { add(id) }
class MatchChunk(override val evidence: Evidence, val match: RuleMatch) : Chunk {
private val justifications = match.justifications().apply { add(evidence) }
override fun justifications(): Justifications = justifications
override var entries: MutableList<Chunk.Entry> = mutableListOf()
override fun entriesLog(): List<Chunk.Entry> = entries
override fun toString() = "(id=$id, $justifications, ${match.rule().tag()}, $entries)"
override fun toString() = "(id=$evidence, ${justifications()}, ${match.rule().tag()}, $entries)"
val ruleUniqueTag: Any get() = match.rule().uniqueTag()
}
class OccChunk(override val id: Int, val occ: Occurrence) : Chunk {
init { occ.justifications.add(id) }
override val justifications: Justs
get() = occ.justifications
class OccChunk(val occ: Occurrence) : Chunk, Justified by occ {
override var entries: MutableList<Chunk.Entry> = mutableListOf()
override fun toString() = "(id=$id, $justifications, activation of $occ)"
override fun toString() = "(id=$evidence, ${justifications()}, activation of $occ)"
}
open class Pos(val chunk: Chunk, val entriesCount: Int) {

View File

@ -29,7 +29,6 @@ import jetbrains.mps.logic.reactor.program.*
import jetbrains.mps.logic.reactor.util.Id
import java.util.*
//typealias Chunk = MatchJournal.Chunk
internal open class MatchJournalImpl(
private val ispec: IncrementalProgramSpec,
@ -38,12 +37,16 @@ internal open class MatchJournalImpl(
// invariant: never empty
private val hist: IteratorMutableList<Chunk>
private var nextChunkId: Int
private var evidenceSeed: Evidence
final override fun nextEvidence(): Evidence = evidenceSeed++
init {
if (view == null) {
nextChunkId = 0
val initChunk = MatchChunk(nextChunkId++, InitRuleMatch)
evidenceSeed = 0
val initChunk = MatchChunk(nextEvidence(), InitRuleMatch)
hist = IteratorMutableList(LinkedList<Chunk>().apply { add(initChunk) })
} else {
// assert that initial chunk is present
@ -51,12 +54,13 @@ internal open class MatchJournalImpl(
assert(this is MatchChunk && match is InitRuleMatch)
}
hist = IteratorMutableList(LinkedList(view.chunks as List<Chunk>))
nextChunkId = view.nextChunkId
evidenceSeed = view.evidenceSeed
}
}
constructor(view: MatchJournal.View? = null) : this(IncrementalProgramSpec.DefaultSpec, view)
// pointer to current position in history where logging (chunk additions) and log erasing (chunk removals) happens
private var posPtr: MutableListIterator<Chunk> = hist.listIterator()
// invariant: always contains valid chunk
@ -70,7 +74,7 @@ internal open class MatchJournalImpl(
var added: MatchChunk? = null
if (ispec.isPrincipal(match.rule())) {
added = MatchChunk(nextChunkId++, match)
added = MatchChunk(nextEvidence(), match)
current = added
posPtr.add(current)
}
@ -86,7 +90,7 @@ internal open class MatchJournalImpl(
var added: OccChunk? = null
if (ispec.isPrincipal(occ.constraint)) {
added = OccChunk(nextChunkId++, occ)
added = OccChunk(occ)
current = added
posPtr.add(current)
}
@ -103,7 +107,7 @@ internal open class MatchJournalImpl(
val rit = hist.listIterator(posPtr.previousIndex())
while (rit.hasPrevious()) {
val prev = rit.previous()
if (prev is MatchChunk && current.justifications.contains(prev.id)) {
if (prev is MatchChunk && current.justifiedBy(prev)) {
return prev
}
}
@ -163,7 +167,7 @@ internal open class MatchJournalImpl(
return
}
if (previous.isDescendantOf(ancestor.id) && !current.isDescendantOf(ancestor.id)) {
if (previous.isDescendantOf(ancestor) && !current.isDescendantOf(ancestor)) {
// reset ptr so that it points after previous chunk
current = previous
posPtr.previous()
@ -178,20 +182,20 @@ internal open class MatchJournalImpl(
override fun dropDescendantsWhile(ancestor: Chunk, dropIf: (Chunk) -> Boolean) {
// starts iterating from the Chunk which is next after current
// leaves 'current' and 'posPtr' intact
val droppedIds = mutableListOf<Int>()
val dropped = mutableListOf<Justified>()
val start = current
while (posPtr.hasNext()) {
current = posPtr.next()
if (!current.isDescendantOf(ancestor.id)) {
if (!current.isDescendantOf(ancestor)) {
break
}
if (dropIf(current)) {
droppedIds.add(current.id)
dropped.add(current)
// no need to 'resetOccurrences' because journal position is left intact
posPtr.remove()
} else if (current.justifications.intersects(droppedIds)) {
} else if (current.justifiedByAny(dropped)) {
// drop descendants of dropped Chunks
posPtr.remove()
}
@ -231,7 +235,7 @@ internal open class MatchJournalImpl(
// Note: returns View for the whole history regardless of current posPtr
override fun view() = MatchJournal.View(ArrayList(hist), nextChunkId)
override fun view() = MatchJournal.View(ArrayList(hist), evidenceSeed)
override fun storeView(): StoreView = StoreViewImpl(allOccurrences())
@ -269,12 +273,12 @@ internal open class MatchJournalImpl(
private class IndexImpl(chunks: Iterable<Chunk>): MatchJournal.Index
{
private val chunkOrder = HashMap<Int, Int>()
private val chunkOrder = HashMap<Evidence, Int>()
private val occChunks = HashMap<Id<Occurrence>, OccChunk>()
init {
chunks.forEachIndexed { index, chunk ->
chunkOrder[chunk.id] = index
chunkOrder[chunk.evidence] = index
if (chunk is OccChunk) {
occChunks[Id(chunk.occ)] = chunk
@ -291,11 +295,11 @@ internal open class MatchJournalImpl(
// the occurrence which activated this match.
match.signature().mapNotNull { occSig ->
occSig?.let { activatingChunkOf(it) }
}.maxBy { chunkOrder[it.id]!! } // compare positions: find latest
}.maxBy { chunkOrder[it.evidence]!! } // compare positions: find latest
// todo: throw for invalid positions?
override fun compare(lhs: Pos, rhs: Pos): Int =
compareBy<Pos>{ chunkOrder[it.chunk.id] }
compareBy<Pos>{ chunkOrder[it.chunk.evidence] }
.thenComparingInt { it.entriesCount }
.compare(lhs, rhs)
}
@ -327,11 +331,11 @@ internal open class MatchJournalImpl(
}
}
fun MatchJournal.justs() = this.currentPos().chunk.justifications
fun MatchJournal.justifications() = this.currentPos().chunk.justifications()
fun RuleMatch.headJustifications(): Justs {
val res: Justs = justsOf()
this.matchHeadKept().forEach { it.justifications().let { res.addAll(it) } }
this.matchHeadReplaced().forEach { it.justifications().let { res.addAll(it) } }
fun RuleMatch.justifications(): Justifications {
val res: Justifications = justsOf()
this.matchHeadKept().forEach { res.addAll( (it as Occurrence).justifications() ) }
this.matchHeadReplaced().forEach { res.addAll( (it as Occurrence).justifications() ) }
return res
}

View File

@ -42,8 +42,4 @@ public interface ConstraintOccurrence {
return null;
}
default
@NotNull
TIntSet justifications() { return new TIntHashSet(); }
}

View File

@ -16,7 +16,6 @@
package jetbrains.mps.logic.reactor.evaluation;
//import gnu.trove.set.TIntSet;
import java.util.List;
@ -26,7 +25,5 @@ public interface MatchJournalChunk {
boolean discarded();
}
// int id();
// TIntSet justifications();
List<Entry> entriesLog();
}

View File

@ -23,7 +23,7 @@ public interface MatchJournalView {
@NotNull
List<MatchJournalChunk> getChunks();
int getNextChunkId();
int getEvidenceSeed();
StoreView getStoreView();
}

View File

@ -174,18 +174,21 @@ fun equals(left: Any, right: Any): ConjBuilder.() -> Unit = {
fun occurrence(id: String, vararg args: Any): Occurrence =
MockConstraint(ConstraintSymbol.symbol(id, args.size))
.occurrence(MockController().logicalStateObservable(), listOf(* args), justsOf(), noLogicalContext)
.occurrence(MockController().logicalStateObservable(), listOf(* args), 0, justsOf(), noLogicalContext)
fun taggedOccurrence(ruleUniqueTag: Any, id: String, vararg args: Any): Occurrence =
MockConstraint(ConstraintSymbol.symbol(id, args.size))
.occurrence(MockController().logicalStateObservable(), listOf(* args), justsOf(), noLogicalContext, ruleUniqueTag)
.occurrence(MockController().logicalStateObservable(), listOf(* args), 0, justsOf(), noLogicalContext, ruleUniqueTag)
fun justifiedOccurrence(id: String, justs: Justs, vararg args: Any): Occurrence =
fun justifiedOccurrence(id: String, evidence: Evidence, justifications: Justifications, vararg args: Any): Occurrence =
MockConstraint(ConstraintSymbol.symbol(id, args.size), true)
.occurrence(MockController().logicalStateObservable(), listOf(* args), justs, noLogicalContext)
.occurrence(MockController().logicalStateObservable(), listOf(* args), evidence, justifications, noLogicalContext)
fun justifiedOccurrence(id: String, justs: Collection<Int>, vararg args: Any): Occurrence =
justifiedOccurrence(id, justsFromCollection(justs), * args)
fun justifiedOccurrence(id: String, evidence: Evidence, justs: Collection<Int>, vararg args: Any): Occurrence =
justifiedOccurrence(id, evidence, justsFromCollection(justs), * args)
fun justifiedOccurrenceInit(id: String, vararg args: Any): Occurrence =
justifiedOccurrence(id, 1, justsFromCollection(setOf(1)), * args)
fun sym0(id: String): ConstraintSymbol =
ConstraintSymbol(id, 0)

View File

@ -1024,7 +1024,7 @@ class TestIncrementalProgram {
// ensure chunks are essentially the same, but have different identities
result.chunksSymbolView() shouldBe oldChunksView
assertNotEquals(result.lastChunk().id, oldLastChunk.id)
assertNotEquals(result.lastChunk().evidence, oldLastChunk.evidence)
}
}
}

View File

@ -48,7 +48,7 @@ class TestStoreAwareJournal {
// log and expand occurrence while tracking its justifications
fun logExpandJustified(id: String, vararg args: Any) =
logExpand(justifiedOccurrence(id, justsCopy(hist.justs()), * args))
logExpand(justifiedOccurrence(id, hist.nextEvidence(), justsCopy(hist.justifications()), * args))
}
@Test
@ -78,16 +78,16 @@ class TestStoreAwareJournal {
{
with(JournalDispatcherHelper(Dispatcher(RuleIndex(rulesLists)))) {
hist.justs() shouldBe justsOf(0)
hist.justifications() shouldBe justsOf(0)
logExpand(justifiedOccurrence("foo", setOf(1)))
logExpand(justifiedOccurrenceInit("foo"))
val fooMatches = d.matches()
fooMatches.count() shouldBe 2
// log first 'foo' match
hist.logMatch(fooMatches.first())
hist.justs() shouldBe justsOf(1,2)
hist.justifications() shouldBe justsOf(1)
logExpandJustified("bar")
d.matches().count() shouldBe 0
@ -95,13 +95,13 @@ class TestStoreAwareJournal {
// log second 'foo' match
hist.logMatch(fooMatches.elementAt(1))
hist.justs() shouldBe justsOf(1,4)
hist.justifications() shouldBe justsOf(1,3)
logExpandJustified("qux")
d.matches().count() shouldBe 1
logFirstMatch()
hist.justs() shouldBe justsOf(1,2,3,4,5,6) // 3 & 5 are ids of OccChunks
hist.justifications() shouldBe justsOf(1,2,3,4,5)
}
}
}
@ -139,7 +139,7 @@ class TestStoreAwareJournal {
val initPos = hist.currentPos()
logExpand(justifiedOccurrence("foo", setOf(1)))
logExpand(justifiedOccurrenceInit("foo"))
val fooPos = hist.currentPos()
@ -213,7 +213,7 @@ class TestStoreAwareJournal {
{
with(JournalDispatcherHelper(Dispatcher(RuleIndex(rulesLists)))) {
logExpand(justifiedOccurrence("foo", setOf(1)))
logExpand(justifiedOccurrenceInit("foo"))
logFirstMatch()
logExpandJustified("bar")
@ -276,7 +276,7 @@ class TestStoreAwareJournal {
logFirstMatch()
// provide initial justification
logExpand(justifiedOccurrence("bar", setOf(1)))
logExpand(justifiedOccurrenceInit("bar"))
logFirstMatch()
logExpandJustified("qux")
@ -330,14 +330,14 @@ class TestStoreAwareJournal {
{
with(JournalDispatcherHelper(Dispatcher(RuleIndex(rulesLists)))) {
logExpand(justifiedOccurrence("foo", setOf(1)))
logExpand(justifiedOccurrenceInit("foo"))
// rule2
logFirstMatch()
logExpand(occurrence("bar"))
logExpand(occurrence("bazz"))
// use this production later, but create it now to get relevant justs
val quxOcc = justifiedOccurrence("qux", hist.justs())
val quxOcc = justifiedOccurrence("qux", hist.nextEvidence(), hist.justifications())
val curChunk = hist.currentPos().chunk
@ -435,7 +435,7 @@ class TestStoreAwareJournal {
with(JournalDispatcherHelper(Dispatcher(RuleIndex(rulesLists)))) {
logExpand(justifiedOccurrence("foo", setOf(1)))
logExpand(justifiedOccurrenceInit("foo"))
// rule1
logFirstMatch()
@ -483,7 +483,7 @@ class TestStoreAwareJournal {
// (bar2 plays a role of the reactivation of original bar)
logExpandJustified("bar2")
// 'bazz' is already expanded from the first execution
hist.justs() shouldBe justsOf(1,2,7) // dependency is only the first chunk, the first activation + new id
hist.justifications() shouldBe justsOf(1,2,7) // dependency is only the first chunk, the first activation + new id
// we have only a single _new_ match; rule3 has been matched already and remains in the history, in future
d.matches().count() shouldBe 1