var MAXIMUM_MEMORY_OF_PREVIOUS_STATES:number = 4096; var MAXIMUM_MEMORY_OF_PREVIOUS_STATES_PER_TERM:number = 16; class SentenceEntry { constructor(s:Sentence, p:string, a:number, time:number) { this.sentence = s; this.provenance = p; this.activation = a; this.time = time; } toString() : string { return "[" + this.sentence + ", " + this.provenance + ", " + this.activation + "]"; } sentence:Sentence; provenance:string; activation:number; time:number; timeEnd:number; // this is just for the sentences that go to the previous sentence list previousInTime:SentenceEntry; // if this sentence has been overwritten by another one, this points to the previous one firstMatch_counter:number = -1; // used to prevent linear search, by marking which sentences have already been retrieved in this cycle allPotentialMatchesWithSentenceForResolution_counter:number = -1; // used to prevent linear search, by marking which sentences have already been retrieved in this cycle } class SentenceContainer { addSentence(s:Sentence, provenance:string, activation:number, time:number) : SentenceEntry { let se:SentenceEntry = new SentenceEntry(s, provenance, activation, time); this.plainSentenceList.push(se); for(let t of s.terms) { let l:SentenceEntry[] = this.sentenceHash[t.functor.name]; if (l == null) { l = []; this.sentenceHash[t.functor.name] = l; } l.push(se); } return se; } addPreviousSentence(s:Sentence, provenance:string, activation:number, time:number, timeEnd:number, current:SentenceEntry) : SentenceEntry { let se:SentenceEntry = new SentenceEntry(s, provenance, activation, time); se.timeEnd = timeEnd; if (current == null) { this.previousSentencesWithNoCurrentSentence.push(se); } else { current.previousInTime = se; this.plainPreviousSentenceList.push(se); } return se; } previousStateSentencesToReplace(t:Term, sign:boolean) : Sentence[] { let results:Sentence[] = []; let l:SentenceEntry[] = this.sentenceHash[t.functor.name]; if (l != null) { for(let se2 of l) { if (se2.sentence.terms.length == 1 && ((se2.sentence.sign[0] && TermContainer.termReplacesPreviousStateTerm(t, se2.sentence.terms[0])) || (se2.sentence.sign[0] != sign && t.equalsNoBindings(se2.sentence.terms[0]) == 1))) { results.push(se2.sentence); } } } return results; } addStateSentenceIfNew(s:Sentence, provenance:string, activation:number, time:number) : boolean { if (s.terms.length != 1) { // console.error("addStateSentenceIfNew: sentence is not a single term sentence! " + s); this.addSentence(s, provenance, activation, time); return true; } // check if we need to replace some sentence: let t:Term = s.terms[0]; let l:SentenceEntry[] = this.sentenceHash[t.functor.name]; let previous_s:SentenceEntry = null; if (l != null) { for(let se2 of l) { if (se2.sentence.terms.length == 1 && ((se2.sentence.sign[0] && TermContainer.termReplacesPreviousStateTerm(t, se2.sentence.terms[0])) || (se2.sentence.sign[0] != s.sign[0] && t.equalsNoBindings(se2.sentence.terms[0]) == 1))) { previous_s = se2; break; } } } if (previous_s == null) { this.addSentence(s, provenance, activation, time); return true; } else { // check if it's different from the previous: if (!s.equalsNoBindings(previous_s.sentence)) { // replace old with new, and store old: // "new_s" is the old, and "previous_s" is the new (this seems confusing, but it's for not having to redo the hash calculation, // since "previous_s" is already in the correct position where we would like the new sentence to be) let new_s:SentenceEntry = new SentenceEntry(previous_s.sentence, previous_s.provenance, previous_s.activation, previous_s.time) new_s.timeEnd = time; new_s.previousInTime = previous_s.previousInTime; this.plainPreviousSentenceList.push(new_s); if (this.plainPreviousSentenceList.length > MAXIMUM_MEMORY_OF_PREVIOUS_STATES) this.plainPreviousSentenceList.splice(0,1); // console.log("plainPreviousSentenceList.length = " + this.plainPreviousSentenceList.length + ", adding: " + s + " to replace " + previous_s.sentence); // we just overwrite, since it's easier (no need to modify plain list nor hash table): previous_s.sentence = s; previous_s.provenance = provenance; previous_s.activation = activation; previous_s.time = time; previous_s.previousInTime = new_s; // Cut chains that are too long: let chainLength:number = 1; let se:SentenceEntry = previous_s; while(se != null) { if (se.previousInTime != null) { chainLength++; if (chainLength >= MAXIMUM_MEMORY_OF_PREVIOUS_STATES_PER_TERM) { let idx:number = this.plainPreviousSentenceList.indexOf(se.previousInTime); if (idx >= 0) { this.plainPreviousSentenceList.splice(idx, 1); //console.log("SPLICED!!! " + idx); } se.previousInTime = null; //console.log("CUT!!!"); //console.log("plainPreviousSentenceList:" + this.plainPreviousSentenceList.length); } } se = se.previousInTime; } //console.log("chainLength: " + chainLength) return true; } } return false; } // Removes all the sentences with a certain provenance (e.g., to clear all coming from perception, of from locations knowledge): removeAllWithProvenance(provenance:string) { let toDelete:SentenceEntry[] = [] for(let se of this.plainSentenceList) { if (se.provenance == provenance) { toDelete.push(se); } } for(let se of toDelete) { this.removeInternal(se); } // Note: we are ignoring the "plainPreviousSentenceList", or "previousSentencesWithNoCurrentSentence"... // - This is because that would involve some expensive search with the current structure... // TODO: add a pointer in SentenceEntry from previous sentence to current, so that I can do this efficiently // ... } removeSentence(s:Sentence) { // console.log("removeSentence " + s.toString()); let found:SentenceEntry = null; for(let t of s.terms) { let l:SentenceEntry[] = this.sentenceHash[t.functor.name]; if (l != null) { if (found == null) { for(let idx:number = 0;idx