/// <summary> /// Get subchain of chainkeys and words that satisfies the passed in condition /// </summary> /// <param name="condition"></param> /// <returns></returns> internal ChainMap GetSubChain(ChainCondition condition) { //If conditions won't be applied return original chains if (condition == null || condition == _alwaysTrueCondition || (condition.WordCondition == null && condition.KeyCondition == null)) { return(_chains); } ChainMap newMap = new ChainMap(); bool valuesFound = false; foreach (ChainKey key in _chains.GetAllKeys()) { if (condition.KeyCondition != null) { if (condition.KeyCondition.Invoke(key)) { foreach (string word in _chains.GetValues(key)) { if (condition.WordCondition != null) { if (condition.WordCondition.Invoke(word)) { newMap.AddToChain(key, word); valuesFound = true; } } } } } } if (!valuesFound) { throw new Exceptions.NoPossibleElements("Chain map filter filtered everything out"); } return(newMap); }
/// <summary> /// Generate a new sentece that contains a minimum number of non punctuation words /// This minimum number of words is not guaranteed, but it is a best try /// </summary> /// <param name="minLength">Minimum length of sentence. Use 0 to indicate /// no minimum</param> /// <returns></returns> public string GenerateSentence(int minLength) { if (minLength < 0) { throw new Exceptions.InvalidArguments("Number of words generated must be positive"); } if (minLength > Defs.MAX_SENTENCE_LENGTH) { throw new Exceptions.InvalidArguments("Minimum sentence length greater than default maximum of " + Defs.MAX_SENTENCE_LENGTH); } StringBuilder sb = new StringBuilder(); //Word generator should reset to beginning of a sentence if needed ChainCondition sentenceStartCondition = new ChainCondition( ck => IsSentenceEnd(ck.Words[ck.Words.Length - 1]) || ck.Words[ck.Words.Length - 1] == null, w => !IsPunctuation(w)); //redundant for clarity if (!_sentenceGenerator.SubchainsInitialized) { _sentenceGenerator.ResetSubchains(sentenceStartCondition, null); } else if (IsSentenceEnd(_sentenceGenerator.CurrentWord)) { //We are already at the beginning of a new sentence //This is to try to optimize perfomance so that we don't do a reset more than necessary //A reset is an expensive operation as all chains need to be parsed _sentenceGenerator.GetNextWord(sentenceStartCondition.WordCondition, Defs.MAX_ITERATIONS); } if (!(sentenceStartCondition.KeyCondition(_sentenceGenerator.CurrKey) && (sentenceStartCondition.WordCondition(_sentenceGenerator.CurrentWord)))) { _sentenceGenerator.ResetReadOnly(); } int countWords; for (countWords = 0; countWords < Defs.MAX_SENTENCE_LENGTH; countWords++) { string nextWord = _sentenceGenerator.CurrentWord; if (!(countWords == 0 || IsPunctuation(nextWord))) { //prepend space if not first word or punctuation sb.Append(" "); sb.Append(nextWord); } else if (countWords == 0) { //capitalize first word if (!string.IsNullOrEmpty(nextWord)) { sb.Append(nextWord.Capitalize()); } } else { sb.Append(nextWord); } //If we get to sentence end, we are done if (IsSentenceEnd(nextWord)) { break; } if (countWords < minLength) { _sentenceGenerator.GetNextWord(w => !IsSentenceEnd(w), Defs.MAX_ITERATIONS); } else { _sentenceGenerator.GetNextWord(); } } if (countWords == Defs.MAX_SENTENCE_LENGTH) { //shouldn't get here, but if we do, put any random sentence end sb.Append(SentenceEnd.FirstOrDefault()); } return(sb.ToString()); }
/// <summary> /// Initialize pool of random keys /// The probability of picking a key is proportional to the number of values in the ChainMap /// </summary> public void Initialize() { _alwaysTrueCondition = new ChainCondition(p => true, p => true); _keys = CreateKeysArray(_chains, _alwaysTrueCondition.KeyCondition); _currentOnEndCondition = _alwaysTrueCondition; }
/// <summary> /// Reset word generator with same condition for initial chain as final chain /// </summary> /// <param name="initialCondition"></param> public void ResetSubchains(ChainCondition initialCondition) { Reset(initialCondition, null); }
/// <summary> /// Reset CurrentWord based on conditions /// </summary> /// <param name="keyCondition"> Condition for key of first word </param> /// <param name="onEndCondition"> Condition for new word after there are no more chains /// By default this is the same as @keyCondition </param> private void Reset(ChainCondition initialCondition, ChainCondition onEndCondition) { ChainCondition realInitialConditions = initialCondition; //Set null conditions to always true conditions to simplify our code if (realInitialConditions == null) { realInitialConditions = _alwaysTrueCondition; } else { if (realInitialConditions.KeyCondition == null) { realInitialConditions.KeyCondition = _alwaysTrueCondition.KeyCondition; } if (realInitialConditions.WordCondition == null) { realInitialConditions.WordCondition = _alwaysTrueCondition.WordCondition; } } ChainCondition realOnEndCondition = onEndCondition; //Set on end condition to start condition if it is null if (realOnEndCondition == null) { realOnEndCondition = realInitialConditions; } else { if (realOnEndCondition.KeyCondition == null) { realOnEndCondition.KeyCondition = _alwaysTrueCondition.KeyCondition; } if (realOnEndCondition.WordCondition == null) { realOnEndCondition.WordCondition = _alwaysTrueCondition.WordCondition; } } ChainMap initialConditionMap; //Only generate subchains if we need to if (realInitialConditions != _alwaysTrueCondition) { initialConditionMap = GetSubChain(realInitialConditions); } else { initialConditionMap = Chains; } ChainKey[] initialConditionSubKeys = CreateKeysArray(initialConditionMap, _alwaysTrueCondition.KeyCondition); //All chains and words should match condition so we can just get random ChainKey currKey = GetRandomKey(initialConditionSubKeys); string currWord; GetCandidate(initialConditionMap.GetValues(currKey), null, out currWord); if (realOnEndCondition != _alwaysTrueCondition) { //Verify on end map has valid elements. If no exception is thrown we are good to go _onEndSubChains = GetSubChain(realOnEndCondition); } else { _onEndSubChains = initialConditionMap; } CurrentWord = currWord; _currKey = currKey; _initialConditionSubChains = initialConditionMap; _initialConditionSubKeys = initialConditionSubKeys; _currentOnEndCondition = realOnEndCondition; SubchainsInitialized = true; }
/// <summary> /// Generate a new sentece that contains a minimum number of non punctuation words /// This minimum number of words is not guaranteed, but it is a best try /// </summary> /// <param name="minLength">Minimum length of sentence. Use 0 to indicate /// no minimum</param> /// <returns></returns> public string GenerateSentence(int minLength) { if (minLength < 0) { throw new Exceptions.InvalidArguments("Number of words generated must be positive"); } if (minLength > Defs.MAX_SENTENCE_LENGTH) { throw new Exceptions.InvalidArguments("Minimum sentence length greater than default maximum of " + Defs.MAX_SENTENCE_LENGTH); } StringBuilder sb = new StringBuilder(); //Word generator should reset to beginning of a sentence if needed ChainCondition sentenceStartCondition = new ChainCondition( ck => IsSentenceEnd(ck.Words[ck.Words.Length - 1]) || ck.Words[ck.Words.Length-1] == null, w => !IsPunctuation(w)) ; //redundant for clarity if(!_sentenceGenerator.SubchainsInitialized) { _sentenceGenerator.ResetSubchains(sentenceStartCondition, null); } else if(IsSentenceEnd(_sentenceGenerator.CurrentWord)) { //We are already at the beginning of a new sentence //This is to try to optimize perfomance so that we don't do a reset more than necessary //A reset is an expensive operation as all chains need to be parsed _sentenceGenerator.GetNextWord(sentenceStartCondition.WordCondition, Defs.MAX_ITERATIONS) ; } if(!(sentenceStartCondition.KeyCondition(_sentenceGenerator.CurrKey) && (sentenceStartCondition.WordCondition(_sentenceGenerator.CurrentWord)))) { _sentenceGenerator.ResetReadOnly(); } int countWords ; for(countWords = 0 ; countWords < Defs.MAX_SENTENCE_LENGTH ; countWords++) { string nextWord = _sentenceGenerator.CurrentWord; if(!(countWords == 0 || IsPunctuation(nextWord))) { //prepend space if not first word or punctuation sb.Append(" ") ; sb.Append(nextWord) ; } else if (countWords == 0) { //capitalize first word if(!string.IsNullOrEmpty(nextWord)) { sb.Append(nextWord.Capitalize()) ; } } else { sb.Append(nextWord) ; } //If we get to sentence end, we are done if(IsSentenceEnd(nextWord)) { break ; } if(countWords < minLength) { _sentenceGenerator.GetNextWord(w => !IsSentenceEnd(w), Defs.MAX_ITERATIONS) ; } else { _sentenceGenerator.GetNextWord() ; } } if(countWords == Defs.MAX_SENTENCE_LENGTH) { //shouldn't get here, but if we do, put any random sentence end sb.Append(SentenceEnd.FirstOrDefault()) ; } return sb.ToString(); }
/// <summary> /// Reset using new initial and onEndCondition. This requires quite a bit of CPU /// as all the chains are processed to get a valid subset for the initial and on end /// conditions /// </summary> public void ResetSubchains(ChainCondition initialCondition, ChainCondition onEndCondition) { Reset(initialCondition, onEndCondition); }
/// <summary> /// Reset CurrentWord based on conditions /// </summary> /// <param name="keyCondition"> Condition for key of first word </param> /// <param name="onEndCondition"> Condition for new word after there are no more chains /// By default this is the same as @keyCondition </param> private void Reset(ChainCondition initialCondition, ChainCondition onEndCondition) { ChainCondition realInitialConditions = initialCondition; //Set null conditions to always true conditions to simplify our code if(realInitialConditions == null) { realInitialConditions = _alwaysTrueCondition; } else { if (realInitialConditions.KeyCondition == null) { realInitialConditions.KeyCondition = _alwaysTrueCondition.KeyCondition; } if (realInitialConditions.WordCondition == null) { realInitialConditions.WordCondition = _alwaysTrueCondition.WordCondition; } } ChainCondition realOnEndCondition = onEndCondition ; //Set on end condition to start condition if it is null if(realOnEndCondition == null) { realOnEndCondition = realInitialConditions ; } else { if (realOnEndCondition.KeyCondition == null) { realOnEndCondition.KeyCondition = _alwaysTrueCondition.KeyCondition; } if (realOnEndCondition.WordCondition == null) { realOnEndCondition.WordCondition = _alwaysTrueCondition.WordCondition; } } ChainMap initialConditionMap ; //Only generate subchains if we need to if(realInitialConditions != _alwaysTrueCondition) { initialConditionMap = GetSubChain(realInitialConditions) ; } else { initialConditionMap = Chains; } ChainKey[] initialConditionSubKeys = CreateKeysArray(initialConditionMap, _alwaysTrueCondition.KeyCondition) ; //All chains and words should match condition so we can just get random ChainKey currKey = GetRandomKey(initialConditionSubKeys) ; string currWord ; GetCandidate(initialConditionMap.GetValues(currKey), null, out currWord) ; if(realOnEndCondition != _alwaysTrueCondition) { //Verify on end map has valid elements. If no exception is thrown we are good to go _onEndSubChains = GetSubChain(realOnEndCondition) ; } else { _onEndSubChains = initialConditionMap; } CurrentWord = currWord; _currKey = currKey; _initialConditionSubChains = initialConditionMap ; _initialConditionSubKeys = initialConditionSubKeys; _currentOnEndCondition = realOnEndCondition; SubchainsInitialized = true ; }
/// <summary> /// Get subchain of chainkeys and words that satisfies the passed in condition /// </summary> /// <param name="condition"></param> /// <returns></returns> internal ChainMap GetSubChain(ChainCondition condition) { //If conditions won't be applied return original chains if(condition == null || condition == _alwaysTrueCondition || (condition.WordCondition == null && condition.KeyCondition == null)) { return _chains ; } ChainMap newMap = new ChainMap() ; bool valuesFound = false ; foreach(ChainKey key in _chains.GetAllKeys()) { if(condition.KeyCondition != null) { if(condition.KeyCondition.Invoke(key)) { foreach(string word in _chains.GetValues(key)) { if(condition.WordCondition != null) { if(condition.WordCondition.Invoke(word)) { newMap.AddToChain(key, word) ; valuesFound = true ; } } } } } } if(!valuesFound) { throw new Exceptions.NoPossibleElements("Chain map filter filtered everything out") ; } return newMap ; }
/// <summary> /// Reset word generator with same condition for initial chain as final chain /// </summary> /// <param name="initialCondition"></param> public void ResetSubchains(ChainCondition initialCondition) { Reset(initialCondition, null) ; }
/// <summary> /// Reset using new initial and onEndCondition. This requires quite a bit of CPU /// as all the chains are processed to get a valid subset for the initial and on end /// conditions /// </summary> public void ResetSubchains(ChainCondition initialCondition, ChainCondition onEndCondition) { Reset(initialCondition, onEndCondition) ; }
/// <summary> /// Initialize pool of random keys /// The probability of picking a key is proportional to the number of values in the ChainMap /// </summary> public void Initialize() { _alwaysTrueCondition = new ChainCondition(p => true, p => true); _keys = CreateKeysArray(_chains, _alwaysTrueCondition.KeyCondition) ; _currentOnEndCondition = _alwaysTrueCondition ; }