943,769 Members | Top Members by Rank

View Poll Results: PE[%]MDAS or PEMD[%]AS ?
Mod should have higher precedence than MD! 0 0%
Mod should have weaker precedence than MD! 0 0%
Voters: 0. You may not vote on this poll

Ad:
  • C++ Discussion Thread
  • Marked Solved
  • Views: 1402
  • C++ RSS
Nov 9th, 2008
0

Where does Modulus fit in with PEMDAS?

Expand Post »
I remade an equation-solver program from Java to C++, and I plan to post it soon but I would rather not until I can determine where modulus fits in with PEMDAS.

To those who don't know, PEMDAS is the order in which mathematical expressions are to be evaluated. The order is

-Paranthesis'
-Exponents
-Mulitplication and Division (same level)
-Addition and Substraction (same level)

so if I have an expression 2 + 3 * 5, according to PEMDAS, 3 * 5 must resolve first so the expression becomes 2 + 15, and the answer afterwards is 17.

Now my problem is that I don't know where Modulus fits in with PEMDAS.

Does it have higher or lower precedence than Multiplication/Division?

8 * 9 % 5 --- what should happen first? 8 * 9 or 9 % 5?

It matters because 8 * 9 first yields 72, and 72 % 5 yields 2 (because 5 subtracts into 72 fourteen times, but cannot subtract into 2 wholly, so 2 is the remainder).

If 9%5 occurs first, 4 is the result for that minor expression and 8 * 4 is 32 so the operator precedence does indeed matter.

I've done some looking up on Google and couldn't find an article to resolve this issue.

Here's my program as it is now--

c++ Syntax (Toggle Plain Text)
  1.  
  2. /**
  3.  * Numerics.cpp
  4.  */
  5. #ifndef NUMERICS
  6. #define NUMERICS
  7.  
  8. #include <sstream>
  9. #include <string>
  10.  
  11. using std::stringstream;
  12. using std::string;
  13.  
  14. /**
  15.  * Convenience class for enabling easy
  16.  * conversions between strings and primitive types.
  17.  *
  18.  * This class is not meant to support non-primitive types,
  19.  * but it should if target class Type has istream and ostream
  20.  * operators overloaded.
  21.  */
  22. namespace Numerics{
  23.  
  24. template<class T>
  25. class Numerical{
  26.  
  27. private:
  28. T number;
  29.  
  30. public:
  31. Numerical(T value = 0) : number(value){}
  32. Numerical(const string& arg){
  33. (*this)(arg);
  34. }
  35.  
  36. /**
  37. * Attempts to assign the argument value to the value
  38. * of this Object's type.
  39. * If the value is invalid, nothing happens.
  40. */
  41. string operator()(const string& arg){
  42. stringstream ss (stringstream::in | stringstream::out);
  43. try{
  44. ss << arg;
  45. ss >> number;
  46. }catch(...){
  47. // currently unsupported
  48. }
  49. return ss.str();
  50. }
  51.  
  52. /**
  53. * Attempts to assign the argument value to the value of
  54. * this Object's type.
  55. */
  56. T operator()(T value){
  57. return (number = value);
  58. }
  59.  
  60. /**
  61. * Returns a string representation of this Object's number
  62. */
  63. string getString(){
  64. stringstream ss (stringstream::in | stringstream::out);
  65. ss << number;
  66. return ss.str();
  67. }
  68.  
  69. /**
  70. * Returns a copy of this Object's number
  71. */
  72. T getValue(){
  73. return number;
  74. }
  75.  
  76. /**
  77. * Extraction operator used to return the underlying value
  78. * during operations assosciated with primitive types.
  79. */
  80. operator T& (){
  81. return number;
  82. }
  83.  
  84. /**
  85. * const version of the above operator. Returns a copy
  86. * of this Object's number.
  87. */
  88. operator T () const{
  89. return number;
  90. }
  91. };
  92.  
  93. /* Some meaningful typedefs for Numerical types */
  94. typedef Numerical<short> Short;
  95. typedef Numerical<unsigned short> U_Short;
  96. typedef Numerical<int> Integer;
  97. typedef Numerical<unsigned int> U_Integer;
  98. typedef Numerical<double> Double;
  99. typedef Numerical<float> Float;
  100. typedef Numerical<char> Byte;
  101. typedef Numerical<unsigned char> U_Byte;
  102. typedef Numerical<wchar_t> Wide_Byte;
  103. typedef Numerical<long int> Long;
  104. typedef Numerical<unsigned long int> U_Long;
  105.  
  106. /* For non-standard types, like __int8, __int16, __int32, and __int64 */
  107. #ifdef ALLOW_NONSTANDARD_PRIMITIVE_TYPES
  108.  
  109. #if (ALLOW_NONSTANDARD_PRIMITIVE_TYPES == 0x01)
  110. typedef Numerical < __int8 > __Int8;
  111. typedef Numerical < unsigned __int8 > U___Int8;
  112. typedef Numerical < __int16 > __Int16;
  113. typedef Numerical < unsigned __int16 > U___Int16;
  114. typedef Numerical < __int32 > __Int32;
  115. typedef Numerical < unsigned __int32 > U___Int32;
  116. typedef Numerical < __int64 > __Int64;
  117. typedef Numerical < unsigned __int64 > U___Int64;
  118. #endif
  119.  
  120. #endif
  121. }
  122.  
  123. #endif


c++ Syntax (Toggle Plain Text)
  1.  
  2. /**
  3.  * EquationSolver.h
  4.  */
  5.  
  6. #ifndef EQUATION_SOLVER_H
  7. #define EQUATION_SOLVER_H
  8.  
  9. #include <string>
  10. #include <vector>
  11.  
  12. using std::string;
  13. using std::vector;
  14.  
  15. namespace EquationHelper{
  16.  
  17. class EquationSolver{
  18. private:
  19. EquationSolver();
  20. static string doOperation(const string&, char, const string&);
  21. static void correctedString(string&);
  22. static void removeSpaces(string&);
  23. static string parse(const string&);
  24. static bool isSolvable(const string&);
  25. static void calculate(vector<string>&, vector<char>&, const string&);
  26.  
  27. public:
  28. static string solve(const string&, int = 50);
  29. };
  30. }
  31. #include "EquationSolver.cpp"
  32.  
  33. #endif

c++ Syntax (Toggle Plain Text)
  1.  
  2. /**
  3.  * EquationSolver.cpp
  4.  */
  5.  
  6. #ifdef EQUATION_SOLVER_H
  7.  
  8. #include <iostream>
  9. #include <cmath>
  10. #include <vector>
  11. #include "Numerics.cpp"
  12.  
  13. using namespace EquationHelper;
  14. using namespace Numerics;
  15. using std::size_t;
  16. using std::vector;
  17. using std::string;
  18. using std::cout;
  19. using std::endl;
  20. using std::ios;
  21.  
  22. typedef EquationSolver ES;
  23.  
  24. /**
  25.  * Private constructor - does nothing.
  26.  */
  27. ES::EquationSolver(){}
  28.  
  29. /**
  30.  * Performs the specified operation against the
  31.  * argument strings. The operation is dependant on
  32.  * the value of op.
  33.  */
  34. string ES::doOperation(const string& lhs, char op, const string& rhs){
  35.  
  36. Double bdLhs = lhs;
  37. Double bdRhs = rhs;
  38. Double temp;
  39. switch(op){
  40. case '^':
  41. temp( pow( bdLhs, bdRhs ) );
  42. break;
  43. case '*':
  44. temp( bdLhs * bdRhs );
  45. break;
  46. case '/':
  47. temp( bdLhs / bdRhs );
  48. break;
  49. case '+':
  50. temp( bdLhs + bdRhs );
  51. break;
  52. case '%':
  53. temp( fmod(bdLhs, bdRhs) );
  54. break;
  55. }
  56. return temp.getString();
  57. }
  58.  
  59. /**
  60.  * Returns the string with its enclosing paranthesis
  61.  * stripped from it.
  62.  */
  63. void ES::correctedString(string& arg){
  64.  
  65. size_t pos1 = arg.find_first_of("(");
  66. size_t pos2 = arg.find_last_of(")");
  67.  
  68. if(pos1 >= 0 && pos1 < arg.length() && pos2 >= 0 && pos2 <= arg.length())
  69. arg[pos1] = arg[pos2] = ' ';
  70. }
  71.  
  72. /**
  73.  * Remove spaces from the argument string.
  74.  */
  75. void ES::removeSpaces(string& argu){
  76.  
  77. string temp = "";
  78. for(size_t i = 0; i < argu.length(); i++)
  79. if(argu[i] != ' ')
  80. temp += argu[i]; // only add non-space characters to temp
  81. argu = temp;
  82. }
  83.  
  84. /**
  85.  * The brains of the program.
  86.  * Solves expressions by using recursion for complex expressions.
  87.  */
  88. string ES::parse(const string& param){
  89.  
  90. string expression = param;
  91. correctedString(expression);
  92. removeSpaces(expression); // Removes paranthesis and spaces from the String expression
  93. string finalExpression = ""; // Placeholder for the final String before values and operands are parsed
  94.  
  95. bool operatorEncountered = true; // Used to determine if the previous value encountered is an operand
  96. for(size_t i = 0; i < expression.length(); i++){ // for all of the characters in the String expression
  97. if(expression[i] == '('){ // if we encounter a left paranthesis, the value must be a nested expression
  98. string placeHolder = "("; // to prevent problems determining during boolean expression-checks
  99. int valuesCounted = 1; // i.e., when should we stop? When valuesCounted is 0?
  100. operatorEncountered = false; // because this will evaluate to be a number, we don't want to consider
  101. // this char-expression to be an operand
  102. for(size_t j = i + 1; valuesCounted != 0; j++){ // We know that i has to be "(" and we've accounted for it, so i+1 is what we'll use
  103. if(expression[j] == '(') // if we encounted a left paranthesis, increment count
  104. valuesCounted++;
  105. else if(expression[j] == ')') // else if its a left paranthesis, decrement count
  106. valuesCounted--;
  107.  
  108. placeHolder += expression[j]; // append the character to the expression
  109. }
  110.  
  111. string evaluatedString = parse(placeHolder); // recursive call - evaluate the nested expression
  112. finalExpression += evaluatedString; // append the evaluatedString to the finalExpression String
  113. i += (placeHolder.length() - 1); // the nested expression is already solved - force i to jump to non-redundant characters
  114. }else{
  115. if(expression[i] == '-' && operatorEncountered == false) // if we encountered a minus sign and we didn't encounter any operands
  116. // before it, changes the subtraction to plus a negative
  117. finalExpression += '+';
  118.  
  119. finalExpression += expression[i]; // append the character to the expression
  120. if((expression[i] == '+'
  121. || expression[i] == '/'
  122. || expression[i] == '^'
  123. || expression[i] == '*'
  124. || expression[i] == '%'
  125. || expression[i] == '-')) // if we encounter a valid operand (including minus), flag operandEncountereed to be true
  126. operatorEncountered = true;
  127. else if(expression[i] != ' ') // else if we dont encounter whitespace nor an operand, flag operand to be false
  128. operatorEncountered = false;
  129. }
  130. }
  131.  
  132. removeSpaces(finalExpression); // for safety measures, removing whitespace again
  133. string perfectExpression = ""; // I'm planning on storing a better version of the finalExpression here
  134.  
  135. for(size_t i = 0; i < finalExpression.length(); i++){ // for every character in the String finalExpression
  136. if((i + 1) < finalExpression.length()) // to prevent overshooting the array, this measure is taken
  137. if(finalExpression[i] == '-' && finalExpression[i + 1] == '-') // if there are two - chars next to each other
  138. i += 2; // ignore them.
  139. perfectExpression += finalExpression[i]; // append to the perfectExpression
  140. }
  141. finalExpression = perfectExpression; // make finalExpression point to the same address as perfectExpression
  142.  
  143. vector<string> totalNumbers; // I'm planning to use this to store Number values (as Strings)
  144. vector<char> totalOperations; // I'm planning to use this to store Operand values (as Characters)
  145. cout << finalExpression << endl; // Technically a debug, but it adds a nice touch to the program.
  146.  
  147. for(size_t i = 0; i < finalExpression.length(); i++){ // for every character in the finalExpression
  148. if(finalExpression[i] >= '0' && finalExpression[i] <= '9'
  149. || finalExpression[i] == '-' || finalExpression[i] == '.'){ // if our character is part of a number
  150. string temp = ""; //
  151. for(size_t j = i; j < finalExpression.length(); j++){ // We're technically breaking good programming practice here--
  152. // I don't intend on traversing the entire String. We're breaking
  153. // out of this loop the moment a non-numeric character is encountered
  154. if(finalExpression[j] >= '0' && finalExpression[j] <= '9'
  155. || finalExpression[j] == '-' || finalExpression[j] == '.'){
  156. temp += finalExpression[j]; // append to the temporary String
  157. }else break;
  158. }
  159. totalNumbers.push_back(temp); // add our collected number to the ArrayList
  160. i += temp.length() == 0 ? 0 : (temp.length() - 1); // we don't want to have redundancy, i.e.
  161. // if number 242 is analyzed, 242, 42 and 2 shouldn't be stored, just 242
  162. // so advance i past numbers already analyzed
  163. }else if(finalExpression[i] == '*'
  164. || finalExpression[i] == '/'
  165. || finalExpression[i] == '^'
  166. || finalExpression[i] == '+'
  167. || finalExpression[i] == '%'
  168. ){ // If we run into an operand-character, store it in the operand list
  169. totalOperations.push_back(finalExpression[i]);
  170. }
  171. }
  172.  
  173. ES::calculate(totalNumbers, totalOperations, "^");
  174. ES::calculate(totalNumbers, totalOperations, "*/%");
  175. ES::calculate(totalNumbers, totalOperations, "+");
  176.  
  177. return totalNumbers[0]; // return the value remaining in the first position of the ArrayList
  178. }
  179.  
  180. /**
  181.  * Calculates the numbers in the first vector using the operands in the 2nd vector,
  182.  * based on the expressions allowed which are determined by the string argument.
  183.  */
  184. void ES::calculate(vector<string>& totalNumbers, vector<char>& totalOperations,
  185. const string& arg){
  186.  
  187. for(int i = 0; i < static_cast<int>(totalOperations.size()); i++){
  188. if( arg.find(totalOperations[i]) != arg.npos){
  189. totalNumbers[i] = doOperation(totalNumbers[i], totalOperations[i], totalNumbers[i + 1]);
  190.  
  191. size_t oldNumberLength = totalNumbers.size();
  192. size_t oldOperatorLength = totalOperations.size();
  193. size_t nextNumberLength = oldNumberLength - 1;
  194. size_t nextOperatorLength = oldOperatorLength - 1;
  195. size_t sCount = 0;
  196. size_t oCount = 0;
  197. vector<string> temp1 ( nextNumberLength );
  198. vector<char> temp2 ( nextOperatorLength );
  199.  
  200. for(size_t j = 0; j < oldNumberLength; j++){
  201. if(j != static_cast<int>(i + 1)){
  202. temp1[sCount++] = totalNumbers[j];
  203. }
  204. if(j != i && j < oldOperatorLength){
  205. temp2[oCount++] = totalOperations[j];
  206. }
  207. }
  208. totalNumbers = temp1;
  209. totalOperations = temp2;
  210.  
  211. i--;
  212. }
  213. }
  214. }
  215.  
  216. /**
  217.  * Returns true if the equation is solvable (not really),
  218.  * returns false otherwise.
  219.  *
  220.  * This function is truly a misnomer, because more restrictions
  221.  * should be put in place.
  222.  */
  223. bool ES::isSolvable(const string& eq){
  224.  
  225. int paranthesisCount = 0; // assuming 0 paranthesis to begin with
  226. for(size_t i = 0; i < eq.length(); i++){ // for every char in the String eq
  227. if(eq[i] == '(') // if the element is a left paranthesis
  228. paranthesisCount++; // increment the paranthesisCount
  229. else if(eq[i] == ')') // else if the element is a right paranthesis
  230. paranthesisCount--; // decrement the paranthesisCount
  231.  
  232. if(paranthesisCount < 0) // if brackets aren't in correct order, return false
  233. return false;
  234. }
  235. return paranthesisCount == 0; // return true if paranthesisCount is zero, otherwise return false
  236. }
  237.  
  238. /**
  239.  * An attempt to solve a string-expression, given
  240.  * a precision value.
  241.  */
  242. string ES::solve(const string& eq, int prec){
  243.  
  244. if(isSolvable(eq)){
  245.  
  246. stringstream ss (stringstream::in | stringstream::out);
  247. cout << eq << endl; // Prints out the equation before it is parsed
  248. string value;
  249.  
  250. value += '(';
  251. value += eq;
  252. value += ')';
  253.  
  254. ss.setf(0, ios::floatfield);
  255. ss.precision(prec);
  256. ss << parse(value);
  257.  
  258. return ss.str(); // returning the final value of the expression
  259. }else return "";
  260. }
  261.  
  262. #endif

c++ Syntax (Toggle Plain Text)
  1.  
  2. /**
  3.  * DriverProgram.cpp
  4.  */
  5.  
  6. /****************************************
  7.  * @Author: Mark Alexander Edwards Jr.
  8.  *
  9.  * This program uses a utility class to solve
  10.  * complex equations represented by string
  11.  * expressions
  12.  ****************************************/
  13. #include <iostream>
  14. #include <ctime>
  15. #include <string>
  16. #include "Numerics.cpp"
  17. #include "EquationSolver.h"
  18.  
  19. using std::cin;
  20. using std::cout;
  21. using std::endl;
  22. using std::flush;
  23. using std::string;
  24. using namespace Numerics;
  25. using namespace EquationHelper;
  26.  
  27. int main(){
  28.  
  29. cout << ES::solve("5 + 3 * (8 - 4)") << endl << endl;
  30. cout << ES::solve("12 / (3 * 4) + 5") << endl << endl;
  31.  
  32. while(true){
  33. cout << "Enter an equation you would \nlike to solve (enter 'exit' to quit): " << flush;
  34. try{
  35. string temp;
  36. getline(cin, temp);
  37. clock_t t1 = clock();
  38. if(temp.compare("exit") == 0) exit(0);
  39. cout << "Answer: " + ES::solve(temp, 4) << endl;
  40. clock_t t2 = clock();
  41. cout << "\nTime taken to calculate value: " <<
  42. (t2-t1) << " Clock cycles" <<endl;
  43. }catch(...){
  44. cout << "Invalid expression! Please try again!" << endl;
  45. }
  46. }
  47.  
  48. cin.ignore();
  49. cin.get();
  50. return 0;
  51. }
Reputation Points: 392
Solved Threads: 108
Posting Shark
Alex Edwards is offline Offline
971 posts
since Jun 2008
Nov 9th, 2008
0

Re: Where does Modulus fit in with PEMDAS?

When in doubt, write a program! It's faster and more accurate than research. I did this on Dev C++, but is it a reasonable assumption that it's the same everywhere?

C++ Syntax (Toggle Plain Text)
  1. #include <iostream>
  2. using namespace std;
  3.  
  4. int main ()
  5. {
  6. int a = 8 * 9 % 5;
  7. cout << a << endl;
  8. a = 10 % 3 * 2;
  9. cout << a << endl;
  10. cin.get ();
  11. return 0;
  12. }

Answers are 2 and 2, so it looks like * and % have equal precedence, so left takes precedence over right, as with multiply and divide. Thus you need another choice in your poll. It's one of the reasons I never liked "PEMDAS". If all you remember is the acronym, you always multiply before dividing, which is wrong, and you always add before subtracting, which is also wrong. So I think it's:

PE[MD%][AS]

Operations inside the brackets have the same precedence.
Last edited by VernonDozier; Nov 9th, 2008 at 4:09 am.
Featured Poster
Reputation Points: 2614
Solved Threads: 687
Posting Expert
VernonDozier is online now Online
5,373 posts
since Jan 2008
Nov 9th, 2008
0

Re: Where does Modulus fit in with PEMDAS?

When in doubt, write a program! It's faster and more accurate than research.
ummm....

google seems to quickly turn up a bunch of results for this question, e.g. http://www.cppreference.com/wiki/operator_precedence
You're right, division/multiplication/modulus all have equal precedence.

... It's one of the reasons I never liked "PEMDAS". If all you remember is the acronym, you always multiply before dividing, which is wrong, and you always add before subtracting, which is also wrong.
I agree, if you multiply before dividing, the answer may be wrong, as in e.g. 2 / 5 * 9
Likewise, if you add before subtracting, e.g. 2 - 5 + 9

However, reversing the above should result in correct answers always: PEDMSA (doesn't really roll off the tongue anymore though)

I'm pretty sure you'll have to use the left-to-right rule for operators where there is >2 operators having equal precedence (i.e. when you add modulus to the mix).
Reputation Points: 85
Solved Threads: 45
Posting Whiz in Training
dougy83 is offline Offline
275 posts
since Jun 2007
Nov 9th, 2008
0

Re: Where does Modulus fit in with PEMDAS?

Now I'm curious...

When would it ever matter if you multiplied before dividing or subtract before adding?

Multiplication and division are technically the same.

Your expression 2 / 5 * 9 is the same as 2 * .2 * 9 so why in the world would it matter if you multiplied first instead of dividing?

Maybe it's because you're using int division?

The same argument exists for subtracting and adding. 2 - 5 + 9 is the same as 2 + -5 + 9 which will yield the same results despite whether you add or subtract first.

Are you using unsigned types?


And if modulus has equal precedence then I suppose my program is technically correct, but I would like it to have either stronger or weaker precedence before MD or after MD.

And Vernon I know you are the mathy-type so I am looking over your post carefully, trying to understand what you mean when you say I need a third option (i.e. Modulus before Division but after Multiplication), when M and D are technically the same?

-Alex
Reputation Points: 392
Solved Threads: 108
Posting Shark
Alex Edwards is offline Offline
971 posts
since Jun 2008
Nov 9th, 2008
0

Re: Where does Modulus fit in with PEMDAS?

Bah nevermind, I'll keep it as it is. Thanks for confirming the precedence of modulus!
Reputation Points: 392
Solved Threads: 108
Posting Shark
Alex Edwards is offline Offline
971 posts
since Jun 2008
Nov 9th, 2008
1

Re: Where does Modulus fit in with PEMDAS?

Now I'm curious...

When would it ever matter if you multiplied before dividing or subtract before adding?

Multiplication and division are technically the same.

Your expression 2 / 5 * 9 is the same as 2 * .2 * 9 so why in the world would it matter if you multiplied first instead of dividing?

Maybe it's because you're using int division?

The same argument exists for subtracting and adding. 2 - 5 + 9 is the same as 2 + -5 + 9 which will yield the same results despite whether you add or subtract first.

Are you using unsigned types?


And if modulus has equal precedence then I suppose my program is technically correct, but I would like it to have either stronger or weaker precedence before MD or after MD.

And Vernon I know you are the mathy-type so I am looking over your post carefully, trying to understand what you mean when you say I need a third option (i.e. Modulus before Division but after Multiplication), when M and D are technically the same?

-Alex
Consider 2 - 5 + 9 and 9 / 3 * 3

(2 - 5) + 9 is different from 2 - (5 + 9)
(9 / 3) * 3 is different from 9 / (3 * 3)

If + and - have equal precedence, and so do * and /, then the left implementations are the correct implementations. If * outranks / and + outranks -, then the implementations on the right are the correct implementations.

Since they DO have equal precedence, 2 - 5 + 9 = (2 - 5) + 9 and
9 / 3 * 3 = (9 / 3) * 3.

It has nothing to do with the fact that they are integers. The option you need in your poll that you don't have is "Multiply, divide, and mod all have the same precedence", which is the correct answer. Thus, what operation occurs first depends on their order from left to right in the term, as the example below:

9 / 3 * 3 = (9 / 3) * 3

Division comes first since the / is to the left of the *.
Featured Poster
Reputation Points: 2614
Solved Threads: 687
Posting Expert
VernonDozier is online now Online
5,373 posts
since Jan 2008
Nov 9th, 2008
0

Re: Where does Modulus fit in with PEMDAS?

Ah! After bending my mind trying to migrate code from one language to another, I completely overlooked this.

Thanks! =)
Reputation Points: 392
Solved Threads: 108
Posting Shark
Alex Edwards is offline Offline
971 posts
since Jun 2008

This thread is solved

Either the thread starter or a moderator has marked this thread as solved. You can most likely trust the responses and answers given. There is most likely no reason for any further responses to be posted here. If you have a related question, please start a new thread in this forum instead.

This thread is more than three months old

No one has posted to this discussion for at least three months. Please let old threads die and do not reply to them unless you feel you have something new and valuable to contribute that absolutely must be added to make the discussion complete. Otherwise, please start a new thread in this forum instead.
Message:
Previous Thread in C++ Forum Timeline: Hash Table Implementation
Next Thread in C++ Forum Timeline: Clock tick





About Us | Contact Us | Advertise | Acceptable Use Policy
Forum Index | Build Custom RSS Feed


Follow us on Twitter


© 2011 DaniWeb® LLC