IndexedAnswers.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. /*
  2. * Copyright (C) 2007-2008, Jeff Thompson
  3. *
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions are met:
  8. *
  9. * * Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * * Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * * Neither the name of the copyright holder nor the names of its contributors
  15. * may be used to endorse or promote products derived from this software
  16. * without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  22. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  23. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  24. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  25. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  26. * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  27. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  28. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. */
  30. using System;
  31. using System.Collections;
  32. using System.Collections.Generic;
  33. namespace OpenSim.Region.ScriptEngine.Shared.YieldProlog
  34. {
  35. /// <summary>
  36. /// An IndexedAnswers holds answers to a query based on the values of index arguments.
  37. /// </summary>
  38. public class IndexedAnswers : YP.IClause
  39. {
  40. // addAnswer adds the answer here and indexes it later.
  41. private List<object[]> _allAnswers = new List<object[]>();
  42. // The key has the arity of answers with non-null values for each indexed arg. The value
  43. // is a list of the matching answers. The signature is implicit in the pattern on non-null index args.
  44. private Dictionary<HashedList, List<object[]>> _indexedAnswers =
  45. new Dictionary<HashedList, List<object[]>>();
  46. // Keeps track of whether we have started adding entries to _indexedAnswers for the signature.
  47. private Dictionary<int, object> _gotAnswersForSignature = new Dictionary<int, object>();
  48. private const int MAX_INDEX_ARGS = 31;
  49. public IndexedAnswers()
  50. {
  51. }
  52. /// <summary>
  53. /// Elements of answer must be ground, since arguments with unbound variables make this
  54. /// into a dynamic rule which we don't index.
  55. /// </summary>
  56. /// <param name="answer"></param>
  57. public void addAnswer(object[] answer)
  58. {
  59. // Store a copy of the answer array.
  60. object[] answerCopy = new object[answer.Length];
  61. Variable.CopyStore copyStore = new Variable.CopyStore();
  62. for (int i = 0; i < answer.Length; ++i)
  63. answerCopy[i] = YP.makeCopy(answer[i], copyStore);
  64. if (copyStore.getNUniqueVariables() > 0)
  65. throw new InvalidOperationException
  66. ("Elements of answer must be ground, but found " + copyStore.getNUniqueVariables() +
  67. " unbound variables");
  68. _allAnswers.Add(answerCopy);
  69. // If match has already indexed answers for a signature, we need to add
  70. // this to the existing indexed answers.
  71. foreach (int signature in _gotAnswersForSignature.Keys)
  72. indexAnswerForSignature(answerCopy, signature);
  73. }
  74. private void indexAnswerForSignature(object[] answer, int signature)
  75. {
  76. // First find out which of the answer values can be used as an index.
  77. object[] indexValues = new object[answer.Length];
  78. for (int i = 0; i < answer.Length; ++i)
  79. {
  80. // We limit the number of indexed args in a 32-bit signature.
  81. if (i >= MAX_INDEX_ARGS)
  82. indexValues[i] = null;
  83. else
  84. indexValues[i] = getIndexValue(YP.getValue(answer[i]));
  85. }
  86. // We need an entry in indexArgs from indexValues for each 1 bit in signature.
  87. HashedList indexArgs = new HashedList(indexValues.Length);
  88. for (int i = 0; i < indexValues.Length; ++i)
  89. {
  90. if ((signature & (1 << i)) == 0)
  91. indexArgs.Add(null);
  92. else
  93. {
  94. if (indexValues[i] == null)
  95. // The signature wants an index value here, but we don't have one so
  96. // we can't add it as an answer for this signature.
  97. return;
  98. else
  99. indexArgs.Add(indexValues[i]);
  100. }
  101. }
  102. // Add the answer to the answers list for indexArgs, creating the entry if needed.
  103. List<object[]> answers;
  104. if (!_indexedAnswers.TryGetValue(indexArgs, out answers))
  105. {
  106. answers = new List<object[]>();
  107. _indexedAnswers[indexArgs] = answers;
  108. }
  109. answers.Add(answer);
  110. }
  111. public IEnumerable<bool> match(object[] arguments)
  112. {
  113. // Set up indexArgs, up to arg position MAX_INDEX_ARGS. The signature has a 1 bit for
  114. // each non-null index arg.
  115. HashedList indexArgs = new HashedList(arguments.Length);
  116. bool gotAllIndexArgs = true;
  117. int signature = 0;
  118. for (int i = 0; i < arguments.Length; ++i)
  119. {
  120. object indexValue = null;
  121. if (i < MAX_INDEX_ARGS)
  122. {
  123. // We limit the number of args in a 32-bit signature.
  124. indexValue = getIndexValue(YP.getValue(arguments[i]));
  125. if (indexValue != null)
  126. signature += (1 << i);
  127. }
  128. if (indexValue == null)
  129. gotAllIndexArgs = false;
  130. indexArgs.Add(indexValue);
  131. }
  132. List<object[]> answers;
  133. if (signature == 0)
  134. // No index args, so we have to match from _allAnswers.
  135. answers = _allAnswers;
  136. else
  137. {
  138. if (!_gotAnswersForSignature.ContainsKey(signature))
  139. {
  140. // We need to create the entry in _indexedAnswers.
  141. foreach (object[] answer in _allAnswers)
  142. indexAnswerForSignature(answer, signature);
  143. // Mark that we did this signature.
  144. _gotAnswersForSignature[signature] = null;
  145. }
  146. if (!_indexedAnswers.TryGetValue(indexArgs, out answers))
  147. yield break;
  148. }
  149. if (gotAllIndexArgs)
  150. {
  151. // All the arguments were already bound, so we don't need to do bindings.
  152. yield return false;
  153. yield break;
  154. }
  155. // Find matches in answers.
  156. IEnumerator<bool>[] iterators = new IEnumerator<bool>[arguments.Length];
  157. foreach (object[] answer in answers)
  158. {
  159. bool gotMatch = true;
  160. int nIterators = 0;
  161. // Try to bind all the arguments.
  162. for (int i = 0; i < arguments.Length; ++i)
  163. {
  164. if (indexArgs[i] != null)
  165. // We already matched this argument by looking up _indexedAnswers.
  166. continue;
  167. IEnumerator<bool> iterator = YP.unify(arguments[i], answer[i]).GetEnumerator();
  168. iterators[nIterators++] = iterator;
  169. // MoveNext() is true if YP.unify succeeds.
  170. if (!iterator.MoveNext())
  171. {
  172. gotMatch = false;
  173. break;
  174. }
  175. }
  176. try
  177. {
  178. if (gotMatch)
  179. yield return false;
  180. }
  181. finally
  182. {
  183. // Manually finalize all the iterators.
  184. for (int i = 0; i < nIterators; ++i)
  185. iterators[i].Dispose();
  186. }
  187. }
  188. }
  189. /// <summary>
  190. /// A HashedList extends an ArrayList with methods to get a hash and to check equality
  191. /// based on the elements of the list.
  192. /// </summary>
  193. public class HashedList : ArrayList
  194. {
  195. private bool _gotHashCode = false;
  196. private int _hashCode;
  197. public HashedList()
  198. : base()
  199. {
  200. }
  201. public HashedList(int capacity)
  202. : base(capacity)
  203. {
  204. }
  205. public HashedList(ICollection c)
  206. : base(c)
  207. {
  208. }
  209. // Debug: Should override all the other methods that change this.
  210. public override int Add(object value)
  211. {
  212. _gotHashCode = false;
  213. return base.Add(value);
  214. }
  215. public override int GetHashCode()
  216. {
  217. if (!_gotHashCode)
  218. {
  219. int hashCode = 1;
  220. foreach (object obj in this)
  221. hashCode = 31 * hashCode + (obj == null ? 0 : obj.GetHashCode());
  222. _hashCode = hashCode;
  223. _gotHashCode = true;
  224. }
  225. return _hashCode;
  226. }
  227. public override bool Equals(object obj)
  228. {
  229. if (!(obj is ArrayList))
  230. return false;
  231. ArrayList objList = (ArrayList)obj;
  232. if (objList.Count != Count)
  233. return false;
  234. for (int i = 0; i < Count; ++i)
  235. {
  236. object value = objList[i];
  237. if (value == null)
  238. {
  239. if (this[i] != null)
  240. return false;
  241. }
  242. else
  243. {
  244. if (!value.Equals(this[i]))
  245. return false;
  246. }
  247. }
  248. return true;
  249. }
  250. }
  251. /// <summary>
  252. /// If we keep an index on value, return the value, or null if we don't index it.
  253. /// </summary>
  254. /// <param name="value">the term to examine. Assume you already called YP.getValue(value)</param>
  255. /// <returns></returns>
  256. public static object getIndexValue(object value)
  257. {
  258. if (value is Atom || value is string || value is Int32 || value is DateTime)
  259. return value;
  260. else
  261. return null;
  262. }
  263. }
  264. }