/// <summary> /// Do operation on operator and value stacks. /// </summary> /// <param name="operators">Operator stack.</param> /// <param name="values">Value stack.</param> /// <param name="express">Express list.</param> private static void Operate(Stack<OperatorSerial> operators, Stack<OperatorSerial> values, List<OperatorSerial> express) { OperatorSerial x = operators.Pop(); OperatorSerial a = values.Pop(); if (x.Code == (int)Logic.Not) { x.Flag = a.Flag; x.LeftOp = a.LeftOp; } else { OperatorSerial b = values.Pop(); x.Flag = (short)((ushort)b.Flag | ((ushort)a.Flag << 1)); x.LeftOp = b.LeftOp; x.RightOp = a.LeftOp; } express.Add(x); // push result to the values stack OperatorSerial value = new OperatorSerial(); value.Flag = (short)(value.Flag & ~OP_LEFT_FEATURE); value.LeftOp = (int)(express.Count - 1); values.Push(value); }
/// <summary> /// Equal. /// </summary> /// <param name="other">Other object to compare with.</param> /// <returns>True if equal, otherwise false.</returns> public bool Equals(OperatorSerial other) { return Code == other.Code && Flag == other.Flag && LeftOp == other.LeftOp && RightOp == other.RightOp; }
internal const short OP_RIGHT_FEATURE = 0x02; // bit1=1 : right operand is feature // bit1=0 : position of operator #endregion #region Static operatons /// <summary> /// Convert expresses as logical string, for example: /// <![CDATA[ 10|~20&30 ]]>. /// </summary> /// <param name="startPosition">First logical operator to take.</param> /// <param name="express">Express list.</param> /// <returns>Logic string.</returns> public static string ToString(int startPosition, OperatorSerial[] express) { if (express == null) { throw new ArgumentNullException("express"); } if (express.Length == 0 || startPosition >= express.Length) { return string.Empty; } OperatorSerial opr = express[startPosition]; if (opr.Code == (int)Logic.End) { return string.Empty; } if (opr.Code == (int)Logic.Value) { return opr.LeftOp.ToString(CultureInfo.InvariantCulture); } string leftOp; if ((opr.Flag & OP_LEFT_FEATURE) != 0) { leftOp = opr.LeftOp.ToString(CultureInfo.InvariantCulture); } else { leftOp = ToString(opr.LeftOp, express); } if (opr.Code == (int)Logic.Not) { uint temp = (uint)(opr.Flag & OP_LEFT_FEATURE); if (temp > 0 || (temp == 0 && express[opr.LeftOp].Code == (int)Logic.Not)) { return "~" + leftOp; } else { return "~(" + leftOp + ")"; } } string rightOp; if ((opr.Flag & OP_RIGHT_FEATURE) != 0) { rightOp = opr.RightOp.ToString(CultureInfo.InvariantCulture); } else { rightOp = ToString(opr.RightOp, express); } if (opr.Code == (int)Logic.And) { uint temp = (uint)(opr.Flag & OP_LEFT_FEATURE); if (temp == 0 && express[opr.LeftOp].Code == (int)Logic.Or) { leftOp = "(" + leftOp + ")"; } temp = (uint)(opr.Flag & OP_RIGHT_FEATURE); if (temp == 0 && (express[opr.RightOp].Code == (int)Logic.Or || express[opr.RightOp].Code == (int)Logic.And)) { rightOp = "(" + rightOp + ")"; } return leftOp + "&" + rightOp; } else { uint temp = (uint)(opr.Flag & OP_RIGHT_FEATURE); if (temp == 0 && express[opr.RightOp].Code == (int)Logic.Or) { rightOp = "(" + rightOp + ")"; } return leftOp + "|" + rightOp; } }
/// <summary> /// Parse logical string to operators. /// </summary> /// <param name="logic">Logic string.</param> /// <returns>Operators.</returns> public static OperatorSerial[] Parse(string logic) { if (string.IsNullOrEmpty(logic)) { throw new ArgumentNullException("logic"); } List<OperatorSerial> express = new List<OperatorSerial>(); Stack<OperatorSerial> operators = new Stack<OperatorSerial>(); Stack<OperatorSerial> values = new Stack<OperatorSerial>(); OperatorSerial op; int index = 0; while (logic.Length > index) { op = new OperatorSerial(); while (logic[index] == ' ') { index++; } if (logic[index] == '~') { op.Code = (int)Logic.Not; operators.Push(op); } else if (logic[index] == '|') { DoHighPriorOperation(operators, values, (int)Logic.Or, express); op.Code = (int)Logic.Or; operators.Push(op); } else if (logic[index] == '&') { DoHighPriorOperation(operators, values, (int)Logic.And, express); op.Code = (int)Logic.And; operators.Push(op); } else if (logic[index] == '(') { op.Code = (int)Logic.Bracket; operators.Push(op); } else if (logic[index] == ')') { DoBracketOperation(operators, values, express); } else if (logic[index] >= '0' && logic[index] <= '9') { op.Code = (int)Logic.Value; op.Flag = (short)OP_LEFT_FEATURE; op.LeftOp = ParseInt(logic, ref index); values.Push(op); DoHighPriorOperation(operators, values, (int)Logic.Not, express); index--; } else { Debug.Assert(false); string message = string.Format(CultureInfo.InvariantCulture, "Invalid logic express [{0}] found.", logic[index]); throw new InvalidDataException(message); } index++; } while (operators.Count > 0) { Operate(operators, values, express); } op = new OperatorSerial(); if (values.Count > 0 && values.Peek().Flag == OP_LEFT_FEATURE) { op = values.Peek(); express.Add(op); } return express.ToArray(); }
/// <summary> /// Load a Cart Node from bindary stream, which is Mulan CRT compatible. /// </summary> /// <param name="br">Binary reader to load CART node.</param> public void Load(BinaryReader br) { if (br == null) { throw new ArgumentNullException("br"); } try { br.ReadInt32(); // Skip the yes child's offset br.ReadInt32(); // Skip the no child's offset NodeType noteType = (NodeType)br.ReadInt32(); if (noteType == NodeType.NotLeaf) { _question = new Question(_metaCart); uint size = br.ReadUInt32(); int startPos = br.ReadInt32(); OperatorSerial[] express = new OperatorSerial[size]; for (uint i = 0; i < size; i++) { express[i] = OperatorSerial.Read(br); } // TODO: parse it into logical presentation string logic = OperatorSerial.ToString(startPos, express); _question.Parse(logic); _leftChild = new CartNode(_metaCart); _leftChild.Load(br); _leftChild.Parent = this; _rightChild = new CartNode(_metaCart); _rightChild.Load(br); _rightChild.Parent = this; QuestionLogic = _question.ToString(); } else if (noteType == NodeType.Leaf) { // AbstractSet int setType = br.ReadInt32(); Debug.Assert(setType == (int)SetType.AbstractSet); int minValue = br.ReadInt32(); int maxValue = br.ReadInt32(); Debug.Assert(maxValue >= minValue); setType = br.ReadInt32(); _setType = (SetType)setType; switch (_setType) { case SetType.BitSet: { // BitSet int size = br.ReadInt32(); Debug.Assert(size == maxValue - minValue + 1); int setCount = br.ReadInt32(); // calculate the number of bytes to allocate to save // the bit set data. the data is INT (4 bytes) aligned int bytesRequired = ((size + 31) >> 5) * 4; byte[] bits = br.ReadBytes(bytesRequired); if (bits.Length != bytesRequired) { string message = string.Format(CultureInfo.InvariantCulture, "Malformed data found, for there is no enough data for Bit set."); throw new InvalidDataException(message); } _unitSet = new BitArray(bits); _unitSet.Length = size; int loadSetCount = 0; for (int k = 0; k < _unitSet.Length; k++) { loadSetCount += _unitSet.Get(k) ? 1 : 0; } Debug.Assert(loadSetCount == setCount); } break; case SetType.IndexSet: { // Count of integer int size = br.ReadInt32(); // Index data _unitSet = new BitArray(maxValue - minValue + 1); _unitSet.SetAll(false); int n = 0; for (int i = 0; i < size; i++) { n = br.ReadInt32(); if (n > _unitSet.Length - 1 || n < 0) { throw new InvalidDataException("Invalid index set data"); } _unitSet.Set(n, true); } } break; default: { throw new InvalidDataException("Invalid set type"); } } } else { Debug.Assert(false); string message = string.Format(CultureInfo.InvariantCulture, "Invalid node type [{0}] of CART tree node found", noteType); throw new InvalidDataException(message); } } catch (EndOfStreamException ese) { string message = string.Format(CultureInfo.InvariantCulture, "Fail to CART tree node from binary stream for invalid data."); throw new InvalidDataException(message, ese); } catch (InvalidDataException ide) { string message = string.Format(CultureInfo.InvariantCulture, "Fail to load CART node from binary stream"); throw new InvalidDataException(message, ide); } }