PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/Sources/LogicCircuit/CircuitTestSocket.cs

#
C# | 299 lines | 272 code | 26 blank | 1 comment | 61 complexity | 7ca320a478954212cf89f430b8ff533b MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics.CodeAnalysis;
  4. using System.Globalization;
  5. using System.Linq;
  6. using System.Numerics;
  7. using System.Threading.Tasks;
  8. namespace LogicCircuit {
  9. public class CircuitTestSocket {
  10. private TableChank chank;
  11. private TableChank[] chankList;
  12. public IEnumerable<InputPinSocket> Inputs { get { return this.chank.Inputs; } }
  13. public IEnumerable<OutputPinSocket> Outputs { get { return this.chank.Outputs; } }
  14. public CircuitTestSocket(LogicalCircuit circuit) {
  15. Tracer.Assert(CircuitTestSocket.IsTestable(circuit));
  16. this.chank = new TableChank(circuit);
  17. if(1 < Environment.ProcessorCount && 15 < this.chank.InputBitCount) {
  18. this.chankList = new TableChank[Environment.ProcessorCount];
  19. BigInteger count = this.chank.Count / this.chankList.Length;
  20. for(int i = 0; i < this.chankList.Length; i++) {
  21. if(i == 0) {
  22. this.chankList[i] = this.chank;
  23. } else {
  24. this.chankList[i] = new TableChank(circuit);
  25. }
  26. this.chankList[i].Count = count;
  27. this.chankList[i].Start = count * i;
  28. }
  29. }
  30. }
  31. public static bool IsTestable(LogicalCircuit circuit) {
  32. IEnumerable<Pin> pins = circuit.CircuitProject.PinSet.SelectByCircuit(circuit);
  33. return pins.Any(p => p.PinType == PinType.Input) && pins.Any(p => p.PinType == PinType.Output);
  34. }
  35. [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters")]
  36. public IList<TruthState> BuildTruthTable(Action<double> reportProgress, Func<bool> keepGoing, Predicate<TruthState> include, int maxCount, out bool truncated) {
  37. System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
  38. watch.Start();
  39. if(this.chankList == null) {
  40. this.chank.BuildTruthTable(reportProgress, keepGoing, include, maxCount);
  41. truncated = this.chank.Trancated;
  42. if(this.chank.Oscillation) {
  43. return null;
  44. }
  45. watch.Stop();
  46. Tracer.FullInfo("CircuitTestSocket.BuildTruthTable", "Single threaded time: {0}", watch.Elapsed);
  47. return this.chank.Results;
  48. }
  49. double[] progress = new double[this.chankList.Length];
  50. Parallel.For(0, this.chankList.Length, i =>
  51. this.chankList[i].BuildTruthTable(
  52. d => {
  53. progress[i] = d;
  54. reportProgress(progress.Sum() / progress.Length);
  55. },
  56. () => keepGoing() && !this.chankList.Take(i).Any(c => c.Trancated) && !this.chankList.Any(c => c.Oscillation),
  57. include,
  58. maxCount
  59. )
  60. );
  61. truncated = this.chankList.Any(c => c.Trancated);
  62. if(this.chankList.Any(c => c.Oscillation)) {
  63. return null;
  64. }
  65. List<TruthState> list = new List<TruthState>();
  66. foreach(TableChank table in this.chankList) {
  67. if(table.Results != null) {
  68. list.AddRange(table.Results);
  69. }
  70. if(maxCount <= list.Count) {
  71. break;
  72. }
  73. }
  74. if(maxCount < list.Count) {
  75. list.RemoveRange(maxCount, list.Count - maxCount);
  76. }
  77. watch.Stop();
  78. Tracer.FullInfo("CircuitTestSocket.BuildTruthTable", "Multi threaded time: {0}", watch.Elapsed);
  79. return list;
  80. }
  81. private class TableChank {
  82. private readonly LogicalCircuit LogicalCircuit;
  83. private readonly CircuitState CircuitState;
  84. public readonly List<InputPinSocket> Inputs = new List<InputPinSocket>();
  85. public readonly List<OutputPinSocket> Outputs = new List<OutputPinSocket>();
  86. public readonly int InputBitCount;
  87. public List<TruthState> Results { get; private set; }
  88. public BigInteger Start = 0;
  89. public BigInteger Count;
  90. public bool Oscillation { get; private set; }
  91. public bool Trancated { get; private set; }
  92. public TableChank(LogicalCircuit logicalCircuit) {
  93. this.LogicalCircuit = TableChank.Copy(logicalCircuit);
  94. this.Plug();
  95. // Create map and state
  96. CircuitMap circuitMap = new CircuitMap(this.LogicalCircuit);
  97. this.CircuitState = circuitMap.Apply(CircuitRunner.HistorySize);
  98. this.Inputs.ForEach(s => s.Function = circuitMap.FunctionConstant(s.Symbol));
  99. this.Outputs.ForEach(s => s.Function = circuitMap.FunctionProbe(s.Symbol));
  100. this.Inputs.Where(s => s.Function == null).ToList().ForEach(s => this.Inputs.Remove(s));
  101. this.Outputs.Where(s => s.Function == null).ToList().ForEach(s => this.Outputs.Remove(s));
  102. Tracer.Assert(this.Inputs.All(s => s.Function != null) && this.Outputs.All(s => s.Function != null));
  103. this.InputBitCount = this.Inputs.Sum(p => p.Pin.BitWidth);
  104. this.Count = BigInteger.One << this.InputBitCount;
  105. circuitMap.TurnOn();
  106. }
  107. public void BuildTruthTable(Action<double> reportProgress, Func<bool> keepGoing, Predicate<TruthState> include, int maxCount) {
  108. this.LogicalCircuit.CircuitProject.InOmitTransaction(() => this.Build(reportProgress, keepGoing, include, maxCount));
  109. }
  110. private void Build(Action<double> reportProgress, Func<bool> keepGoing, Predicate<TruthState> include, int maxCount) {
  111. this.Results = new List<TruthState>();
  112. this.Oscillation = false;
  113. this.Trancated = false;
  114. int inputCount = this.Inputs.Count;
  115. int outputCount = this.Outputs.Count;
  116. if(0 < inputCount && 0 < outputCount) {
  117. BigInteger end = this.Start + this.Count;
  118. BigInteger onePercent = this.Count / 100;
  119. BigInteger count = 0;
  120. double progress = 0;
  121. TruthState state = new TruthState(inputCount, outputCount);
  122. for(BigInteger value = this.Start; value < end; value++) {
  123. if(maxCount <= this.Results.Count || !keepGoing()) {
  124. this.Trancated = true;
  125. break;
  126. }
  127. int bit = 0;
  128. for(int i = this.Inputs.Count - 1; 0 <= i; i--) {
  129. InputPinSocket pin = this.Inputs[i];
  130. int v = (int)((value >> bit) & int.MaxValue) & (pin.Pin.BitWidth < 32 ? (1 << pin.Pin.BitWidth) - 1 : ~0);
  131. pin.Function.Value = v;
  132. Tracer.Assert(pin.Function.Value == v, "Value get truncated");
  133. bit += pin.Pin.BitWidth;
  134. }
  135. if(!this.CircuitState.Evaluate(true)) {
  136. this.Oscillation = true;
  137. break;
  138. }
  139. for(int i = 0; i < inputCount; i++) {
  140. state.Input[i] = this.Inputs[i].Function.Value;
  141. }
  142. for(int i = 0; i < outputCount; i++) {
  143. state.Result[i] = this.Outputs[i].Function.Pack();
  144. }
  145. if(!state.Unpack(this.Outputs.Select(o => o.Function.ParameterCount).ToArray()) || include == null || include(state)) {
  146. this.Results.Add(state);
  147. state = new TruthState(inputCount, outputCount);
  148. }
  149. if(reportProgress != null) {
  150. count++;
  151. if(onePercent < count) {
  152. count = 0;
  153. if(onePercent == BigInteger.Zero) {
  154. Tracer.Assert(0 < this.Count && this.Count < 100);
  155. reportProgress((double)((value + 1 - this.Start) * 100) / (double)this.Count);
  156. } else {
  157. reportProgress(Math.Min(++progress, 100));
  158. }
  159. }
  160. }
  161. }
  162. }
  163. }
  164. private static LogicalCircuit Copy(LogicalCircuit circuit) {
  165. LogicalCircuit other = null;
  166. CircuitProject copy = new CircuitProject();
  167. copy.InTransaction(() => {
  168. copy.ProjectSet.Copy(circuit.CircuitProject.ProjectSet.Project);
  169. other = copy.LogicalCircuitSet.Copy(circuit, true);
  170. });
  171. foreach(CircuitSymbol symbol in other.CircuitSymbols()) {
  172. symbol.GuaranteeGlyph();
  173. }
  174. return other;
  175. }
  176. private void Plug() {
  177. List<Pin> pins = this.LogicalCircuit.CircuitProject.PinSet.SelectByCircuit(this.LogicalCircuit).ToList();
  178. pins.Sort(PinComparer.Comparer);
  179. this.LogicalCircuit.CircuitProject.InTransaction(() => {
  180. foreach(Pin pin in pins) {
  181. if(pin.PinType == PinType.Input) {
  182. this.Inputs.Add(new InputPinSocket(pin));
  183. } else if(pin.PinType == PinType.Output) {
  184. this.Outputs.Add(new OutputPinSocket(pin));
  185. } else {
  186. Tracer.Fail();
  187. }
  188. }
  189. });
  190. }
  191. }
  192. }
  193. public class InputPinSocket {
  194. public Pin Pin { get; private set; }
  195. public Constant Value { get; private set; }
  196. public CircuitSymbol Symbol { get; private set; }
  197. public FunctionConstant Function { get; set; }
  198. public InputPinSocket(Pin pin) {
  199. Tracer.Assert(pin.PinType == PinType.Input);
  200. CircuitProject project = pin.CircuitProject;
  201. this.Pin = pin;
  202. this.Value = project.ConstantSet.Create(pin.BitWidth, 0, PinSide.Right);
  203. CircuitSymbol pinSymbol = project.CircuitSymbolSet.SelectByCircuit(pin).FirstOrDefault();
  204. Tracer.Assert(pinSymbol != null);
  205. this.Symbol = project.CircuitSymbolSet.Create(this.Value, pin.LogicalCircuit, pinSymbol.X, pinSymbol.Y);
  206. this.Symbol.Rotation = pinSymbol.Rotation;
  207. pinSymbol.X = pinSymbol.Y = int.MinValue;
  208. }
  209. }
  210. public class OutputPinSocket {
  211. public Pin Pin { get; private set; }
  212. public CircuitProbe Value { get; private set; }
  213. public CircuitSymbol Symbol { get; private set; }
  214. public FunctionProbe Function { get; set; }
  215. public OutputPinSocket(Pin pin) {
  216. Tracer.Assert(pin.PinType == PinType.Output);
  217. CircuitProject project = pin.CircuitProject;
  218. this.Pin = pin;
  219. this.Value = project.CircuitProbeSet.Create(null);
  220. CircuitSymbol pinSymbol = project.CircuitSymbolSet.SelectByCircuit(pin).FirstOrDefault();
  221. Tracer.Assert(pinSymbol != null);
  222. this.Symbol = project.CircuitSymbolSet.Create(this.Value, pin.LogicalCircuit, pinSymbol.X, pinSymbol.Y);
  223. this.Symbol.Rotation = pinSymbol.Rotation;
  224. pinSymbol.X = pinSymbol.Y = int.MinValue;
  225. }
  226. }
  227. [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")]
  228. public struct TruthState {
  229. private readonly int[] input;
  230. private readonly long[] result;
  231. private int[] output;
  232. private int[] bitWidth;
  233. [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
  234. public int[] Input { get { return this.input; } }
  235. [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
  236. public long[] Result { get { return this.result; } }
  237. [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
  238. public int[] Output { get { return this.output; } }
  239. public TruthState(int inputCount, int outputCount) {
  240. this.input = new int[inputCount];
  241. this.result = new long[outputCount];
  242. this.output = null;
  243. this.bitWidth = null;
  244. }
  245. public bool Unpack(int[] bitWidthList) {
  246. Tracer.Assert(bitWidthList.Length == this.result.Length);
  247. this.bitWidth = bitWidthList;
  248. int[] res = new int[this.result.Length];
  249. for(int i = 0; i < res.Length; i++) {
  250. int unpacked;
  251. if(!FunctionProbe.ToInt(this.result[i], this.bitWidth[i], out unpacked)) {
  252. return false;
  253. }
  254. res[i] = unpacked;
  255. }
  256. this.output = res;
  257. return true;
  258. }
  259. public string this[int index] {
  260. get {
  261. if(this.output != null) {
  262. return this.output[index].ToString("X", CultureInfo.InvariantCulture);
  263. }
  264. int unpacked;
  265. if(FunctionProbe.ToInt(this.result[index], bitWidth[index], out unpacked)) {
  266. return unpacked.ToString("X", CultureInfo.InvariantCulture);
  267. }
  268. long res = this.result[index];
  269. return CircuitFunction.ToText(Enumerable.Range(0, this.bitWidth[index]).Select(i => (State)((res >> i * 2) & 0x3)), false);
  270. }
  271. }
  272. }
  273. }