Sunday, 25 February 2018

Chapter 7 // Drill 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 - Principles & Practice Using C++

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 7 // Drill 1


1. Starting from the file calculator08buggy.cpp, get the calculator to compile.


The program will not compile due to 1 error in Token Token_stream::get() near the end where "return Token(name, s);". The error given says "no instance of Token::Token matches argument list". This is because the two functions in struct Token only except a char or a char and a double. Line changed to "Token(name)"

In the actual file itself it says there are 3 errors that the compiler will catch however, mine only caught 1 that needed fixing before it could compile.

Chapter 7 // Drill 2

2. Go through the entire program and add appropriate comments.

These will be seen below with the entire code.

Chapter 7 // Drill 3


3. As you commented, you found errors (deviously inserted especially for you to find). Fix them; they are not in the text of the book.

  1. If s == 'quit' then token should return quit, not name.
  2. In primary, if '(' is used without ')' the error should return "expected ')'" 
  3. In primary, case '(' was missing a return or break. d should be returned.
  4. Modulo % was given as a case however there is no code to define what to do with it.

Chapter 7 // Drill 4, 5

4. Testing: Prepare a set of inputs and use them to test the calculator
5. Do the testing ad fix any bugs that you missed when you commented

I'm not going to show the testing report however I did find one bug. The 'let' case is inputting everything not already defined as the exact same thing. For example if you put "let k = 1000" then would r = 1000 and j = 1000 and so on. I realised this was because get() was not returning the string s and applying it to name; it was returning an empty string because I had changed the return to Token(name) in drill 1 to get it to compile. Turns out the Token function which returns a char and string was missing in the struct so that was added.

I'm sure there are more however I have limited time to do these exercises now so I spent as much time as I could combing through the code. I feel I fixed most of them when commenting anyway.

Chapter 7 // Drill 6

6. Add a predefined name k meaning 1000

The define_name() function had to be added in for this to work, it can be found on page 246 at the top. Then we can define as many names as we want in main().

Chapter 7 // Drill 7, 8

7. Give the user a square root function: sqrt(). Use the standard library. Remember to update comments including grammar.
8. Catch attempts to take square root of a negative number and print and appropriate error message.

I added the following to the get() function in the default section:

if (s == "sqrt")
     return Token(squareR);

squareR is const char defined earlier with the other const chars, I set the char to 's'.

Then in primary() the following case was added to solve for "sqrt()":


//solve "sqrt(expression)"
case squareR:
{
//get next char after 'sqrt' if not '(' then error
t = ts.get();
if (t.kind != '(')
error("'(' expected");

  //if expression is less than 0 print an error
double d = expression();
if (d < 0)
error("Cannot square root negative integers");

  //get next char after expression, if not ')' then error
t = ts.get();
if (t.kind != ')')
error("')' expected");

  //return square root of the expression taken from the tokenstream
return sqrt(d);

}

I originally tried to define sqrt as a variable however changing the value depending on what was inputted was a little to cumbersome. We already solve for parenthesis so I added another case under "let" and "quit" in get() to tell the program to do something else if the user types in "sqrt". Then in that case it is a simple as checking if the next character in the stream is '(', if it's not; error. The case then solves for an expression or whatever may be between the brackets, just as in the case for '('. We give an error for negative numbers and another if ')' is not provided. The case then returns the square root of the number found by using the sqrt() function found in <cmath>.


Chapter 7 // Drill 9

9. Allow the user to use pow(x, i) to mean "Multiply x with itself i times": for example, pow(2.5,3) is 2.5*2.5*2.5. Require i to be an integer using the technique we used for %.

This is simply modifying the case for square root. I added another const char called findPow and assigned it 'p'. Then in get() I added another case to solve for ',' and a couple lines underneath squareRoot:

if (s == "pow")

     return Token(findPow);

Then in primary the following case was added to solve for "pow(,)":

//solve "pow(expression, expression)"
case findPow:
{
//get next char after 'pow' if not '(' then error 
t = ts.get();
if (t.kind != '(')
error("'(' expected");

//get the expression after '('
double d = expression();

//get next char after 'expression' if not ',' then error 
t = ts.get();
if (t.kind != ',')
error("',' expected");

//get the expression after ','
double i = expression();

//get next char after expression, if not ')' then error
t = ts.get();
if (t.kind != ')')
error("')' expected");

// return expression using pow() from <cmath>
return pow(d, i);

}

It is a bit hacky, I don't like the repeated getting of '('; I may turn that into a function for the final version which I will be putting at the bottom of the post. As you can see though it is very similar to solving for 'sqrt' as it's just a case of telling the program to get another character and see what it is. 



Chapter 7 // Drill 10

10. Change the "declaration keyword" from let to #.

By doing this there is a problem in that # is not a letter so it will not get to the default section of get() and the compiler will return "bad token" if # is not solved for correctly. To get round this simply create another case in get():

case '#':
                  return Token(let);

I left the string version in the default section though just in case we ever want to go back to using "let" instead of "#". But it won't interfere with the code so it's OK to leave to it there.


Chapter 7 // Drill 11

11. Change the "quit keyword" from quit to exit. That will involve defining a string for quit just as we did for let.

For this we can simply create a few const strings underneath where we define the const chars like so:

const string declKey = "let";      //unused as of drill 10
const string quitKey = "quit";
const string sqrtKey = "sqrt";

const string powKey = "pow";

Then we can adjust the code in get() to use these strings instead of actual strings meaning this is the only place we have to change in the code now if we decide to change the meaning of any other keywords at anytime.


Finished Code

Here is my complete finished code from completing all the drills in chapter 7:

// pandp.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "std_lib_facilities.h"

//user defined type to hold name-value pair for use in calculator
struct Token {
char kind;
double value;
string name;
Token(char ch) :kind(ch), value(0) { }
Token(char ch, double val) :kind(ch), value(val) { }
Token(char ch, string n) :kind(ch), name(n) { }
};

//user-defined type that handles retrieving items from input
class Token_stream {
bool full;
Token buffer;
public:
Token_stream() :full(0), buffer(0) { }

Token get();
void unget(Token t) { buffer = t; full = true; }

void ignore(char);
};

const char let = 'L';
const char quit = 'Q';
const char print = ';';
const char number = '8';
const char name = 'a';
const char squareR = 's';
const char findPow = 'p';

const string declKey = "#";
const string quitKey = "quit";
const string sqrtKey = "sqrt";
const string powKey = "pow";

//evaluate each char in the stream and determine what it is 
Token Token_stream::get()
{
if (full) //check if we have already have a token ready
full = false; 
return buffer; 
}

char ch;
cin >> ch; //skips whitespace
switch (ch) 
{
case '(': case ')': case '+': case '-':
case '*': case '/': case '%': case ';':
case '=': case ',':
return Token(ch); //let each char represent itself
case '.':
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
case '8': case '9':
{
cin.unget(); //put digit back into the input stream
double val;
cin >> val; //read a floating-point number
return Token{ number, val }; //return number or . with a value, put back into buffer
}

//allow user defined variables if user types #
case '#':
return Token(let);
default:
//do this if ch is a letter
if (isalpha(ch)) 
{
string s;
s += ch;

//while there are still chars in cin, read them into s
while (cin.get(ch) && (isalpha(ch) || isdigit(ch))) 
s += ch;
cin.unget();

//if string is equal to other commands defined below, return them
if (s == declKey) 
return Token(let);
if (s == quitKey) 
return Token(quit);
if (s == sqrtKey)
return Token(squareR);
if (s == powKey)
return Token(findPow);

return Token(name, s);
}

//if the char does not fit any of these parameters return an error message
error("Bad token");
}
}

//discard characters up to and including a c
//c represents the kind of token
void Token_stream::ignore(char c)
{
//first look in the buffer
if (full && c == buffer.kind) 
{
full = false;
return;
}

full = false;

//now search input
char ch;
while (cin >> ch)
if (ch == c) return;
}

struct Variable 
{
string name;
double value;
Variable(string n, double v) :name(n), value(v) { }
};

vector<Variable> names;

//return the value of the Variable named s
double get_value(string s, double val)
{
for (int i = 0; i < names.size(); ++i)
{
if (names[i].name == s)
{
return names[i].value;
}
}

error("get: undefined name ", s);
}

//set the Variable named s to d
void set_value(string s, double d)
{
for (int i = 0; i <= names.size(); ++i)
if (names[i].name == s) 
{
names[i].value = d;
return;
}

error("set: undefined name ", s);
}

//is variable already declared?
bool is_declared(string s)
{
for (int i = 0; i < names.size(); ++i)
{
if (names[i].name == s) 
return true;
}

return false;
}

//add (var,val) to variable vector
double define_name(string var, double val)
{
if (is_declared(var))
error(var, " declared twice");

names.push_back(Variable(var, val));

return val;
}

Token_stream ts;

double expression(); //forward declaration

//check tokenstream for 'your char here'
Token checkForChar(Token t, char ch)
{
  //convert ch to string for error message string chstring = ""; chstring += ch; error("'" + chstring + "' expected");
}

//read in values that are accepted for first time use
double primary()
{
//get character from stream
Token t = ts.get();
switch (t.kind) 
{
//solve "(expression)"
case '(':
{
double d = expression();
t = ts.get();
checkForChar(t, ')');
return d;
}

//solve "-primary"
case '-':
return -primary();

//solve "number"
case number:
return t.value;

//solve "let name = value"
case name:
return get_value(t.name, 0);

//solve "sqrt(expression)"
case squareR:
{
//get next char after 'sqrt' if not '(' then error 
t = ts.get();
checkForChar(t, '(');

//if expression is less than 0 print an error
double d = expression();
if (d < 0)
error("Cannot squareroot negative integers");

//get next char after expression, if not ')' then error
t = ts.get();
checkForChar(t, ')');

// return square root of the expression taken from the tokenstream
return sqrt(d);
}

//solve "pow(expression, expression)"
case findPow:
{
//get next char after 'pow' if not '(' then error 
t = ts.get();
checkForChar(t, '(');

//get the expression after '('
double d = expression();

//get next char after 'expression' if not ',' then error 
t = ts.get();
checkForChar(t, ',');

//get the expression after ','
double i = expression();

//get next char after expression, if not ')' then error
t = ts.get();
checkForChar(t, ')');

// return expression using pow() from <cmath>
return pow(d, i);
}
default:
error("primary expected");
}
}

//read in values that would normally come after a primary
double term()
{
double left = primary();
while (true) 
{
Token t = ts.get();
switch (t.kind) 
{
case '*':
left *= primary();
break;
case '/':
{
double d = primary();
if (d == 0) 
error("divide by zero");
left /= d;
break;
}
case '%':
{
double d = primary();
if (d == 0)
error("%:divide by zero");
left = fmod(left, d);
break;
}
default:
ts.unget(t);
return left;
}
}
}

//can be used before a primary
double expression()
{
double left = term();
while (true) 
{
Token t = ts.get();

switch (t.kind) 
{
case '+':
left += term();
break;
case '-':
left -= term();
break;
default:
ts.unget(t);
return left;
}
}
}

//check for name definition errors
double declaration()
{
Token t = ts.get();
if (t.kind != 'a') 
error("name expected in declaration");

string name = t.name;
if (is_declared(name)) 
error(name, " declared twice");

Token t2 = ts.get();
if (t2.kind != '=') 
error("= missing in declaration of ", name);

double d = expression();
names.push_back(Variable(name, d));

return d;
}


double statement()
{
Token t = ts.get();
switch (t.kind) 
{
case let:
return declaration();
default:
ts.unget(t);
return expression();
}
}

void clean_up_mess()
{
ts.ignore(print);
}

const string prompt = "> ";
const string result = "= ";

void calculate()
{
while (true) try 
{
cout << prompt;
Token t = ts.get();

while (t.kind == print) 
t = ts.get(); //first discard all 'prints'

if (t.kind == quit) 
return;

ts.unget(t);

cout << result << statement() << endl;
}
catch (runtime_error& e) 
{
cerr << e.what() << endl;
clean_up_mess();
}
}

int main()
try {
define_name("pi", 3.1415926535);
define_name("e", 2.7182818284);
define_name("k", 1000);

calculate();
return 0;
}

catch (exception& e) {
cerr << "exception: " << e.what() << endl;
char c;
while (cin >> c && c != ';');
return 1;
}

catch (...) {
cerr << "exception\n";
char c;
while (cin >> c && c != ';');
return 2;

}

Saturday, 24 February 2018

Where does the time go?

So my last post on this blog was the 1st of June 2017. It's disheartening to see I only posted twice last year however I found my first year of uni a little overwhelming. Since the last post I've completed my first year and I'm now four months away from completing my second and receiving a foundation degree. I'll definitely be doing the honours top-up to receive a full bachelors degree next year however the course I'm doing is heavily focused on video-game design with some programming involved. Over the past year and half I've realised that I much prefer programming over making games. 

With the industry moving towards making games in pre-made engines such as Unreal, Unity, CryEngine or their own in-house engines like RAGE and Snowdrop it would appear the art of programming a game from absolute scratch is a thing of the past. Sure you can but it's something that indie devs would do and if you're an indie dev (like I wanted to be) it's hard to complete a full game without the use of an engine like game maker whilst paying the bills. 

I've become a big fan of C and procedural programming as it follows how my brain works. I like to make lists and follow those lists; ticking one task off at a time. Object-Orientated programming is more than a little confusing to me as it jumps around all over the place. I recently just finished implementing a font class for my DirectX 11 engine and the amount of times I had to go back and change function declarations; create new functions or just redesign the entire thing entirely did my head in. Perhaps it just goes to show how weak my C++ skills are right now, I'm certainly more comfortable writing class interfaces now than I was a year ago. A year ago, trying to implement code from MSDN seemed impossible so perhaps I'm just being impatient with myself.

I've decided to do a masters degree in Computer Science once I've finished my bachelors as it's more geared towards data science, memory management and general programming which I'm much more interested in now. The past year of my degree has been filled with writing game design documents, researching mobile input methods and 3D modelling; the latter I detest with a passion. I'm also not a big fan of Unreal and I find it dissatisfying to have to use it constantly throughout my degree. I am however enjoying building my own engine using C++ and DirectX, there is just something about writing code and clicking the build button and it compiles with no issues.

I took this blog off google for while as I wondered if I should continue it, however I am determined to finish this book and his next one which is for more advanced C++ programmers. I will begin re-posting however at a slower pace as I have many deadlines looming. I would like to finish this book by the end of summer though. Thank you to everyone who drops by and I hope someone finds these posts helpful.

Thursday, 1 June 2017

Chapter 6 // Exercise 10 - Principles & Practice Using C++

Chapter 6 // Exercise 10


"Design a program that asks users for two numbers, and asks them whether they want to calculate permutations or combinations, and prints out the result. This will have several parts. Do an analysis of the above requirements. Write exactly what the program will have to do. Then, go into the design phase. Write pseudo code for the program and break it into sub-components. This program should have error-checking. Make sure that all erroneous inputs will generate good error messages."

OK, at first this sounds pretty daunting and there are some rather large(ish) equations given in the book however when you actually break down what he's asking, it becomes a pretty simple task.

We have already been given the formulas so it's just a matter of getting some numbers from the user and throwing them at some functions which solve the formulas, the pseudo code could look like this:

Ask user for two numbers
>numbers
Does user want combination or permutation?
>answer

If answer == Combination
       pass numbers to function and place into equation
       print combinations

If answer == Permutation
       pass numbers to function and place into equation
       print permutations

Pause & Quit

For some more information on combinations and permutations, check out this site. After reading this, it would appear that there are two ways to solve each problem but Bjarne only gives the formula to solve combinations without repetition. According to the site above, solving combinations with repetition requires a slightly different formula. So which one do we use?

For this exercise, I'm just going to stick to the formulas given.



#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <cctype>

#undef max //undefine max so it can be used in getInt

using namespace std;

double a;
double b;

//this function was taken from http://www.cplusplus.com/forum/beginner/21595/
//it makes sure that only numbers can be used
double getInt()
{
  double x = 0;

  while (!(cin >> x)) 
  {
    cin.clear();
    cin.ignore(numeric_limits<streamsize>::max(), '\n');
    cout << "You need to input a number: ";
 }

 return x;
}

//this gets two numbers and asks if the users wants permutations or combinations
//erroneous input is checked by getInt() and only allows P or C to be chosen
char permOrComb()
{
  cout << "Please enter the maximum amount of numbers: " << endl;
  a = getInt();
  cout << "Please enter how many numbers are needed: " << endl;
  b = getInt();

  cout << "\n\nWould you like to perform Permutation (p) or Combination (c)?: " << endl;
  char answer;
  cin >> answer;
  answer = toupper(answer); //converts letter to upper case

  while (answer != 'P' && answer != 'C')
  {
    cout << "You need to enter (p) or (c): ";
    cin >> answer;
    answer = toupper(answer);
  }

  return answer;
}

//this works out a factorial and returns the result
double getFactorial(double n)
{
  if (n == 0)
     n = 1;

  double factorial = n;

  for (double i = n - 1; i > 1; --i)
  {
    factorial = factorial*i;
  }

  return factorial;
}

//this solves permutations
double getPermutation()
{
  double p;
  double temp = a - b;

  //if a - b is a minus, flip it to positive
  if (temp < 0)
    temp *= -1;

  p = (getFactorial(a) / getFactorial(temp));

  return p;
}

//this solves combinations (where there is no repitition)
double getCombination()
{
  double c;
  double tempP = getPermutation();

  c = (tempP) / (getFactorial(b));
 
  return c; 
}

bool getQuit()
{
  char temp;
  bool qTemp = true;

  cout << "\n\nWould you like to quit (q) or continue (c): ";
  cin >> temp;
  temp = toupper(temp);

  while (temp != 'Q' && temp != 'C')
  {
    cout << "You need to enter (q) or (c): ";
    cin >> temp;
    temp = toupper(temp);
  }

  if (temp == 'Q')
    qTemp = false;
  if (temp == 'P')
    qTemp = true;

  return qTemp;
}


int main()
{
  bool quit = true;

  while (quit)
  {
    char answer = permOrComb();

    if (answer == 'P')
    {
      double temp = getPermutation();
      cout << "\nThere are " << temp << " permutations with these numbers.\n";
      quit = getQuit();
    }

   if (answer == 'C')
   {
     double temp = getCombination();
     cout << "\nThere are " << temp << " combinations with these numbers.\n";
     quit = getQuit();
   }

  }

  return 0;
}


I used the factorial function I created in Chapter 6 - Exercise 3 to solve for ! and then it was simply a case of checking for erroneous input. 

As I said above, this only solves combinations where there is no repetition, but it would be simple to create another function which solves for that using the formula given from the link above. Then just ask the user if repetition is allowed or not.

Monday, 26 December 2016

Chapter 6 // Exercise 9 - Principles & Practice Using C++

Chapter 6 // Exercise 9

Write a program that reads digits and composes them into integers. For example 123 is read as the characters 1, 2 and 3. The program should output "123 is 1 hundred and 2 tens and 3 ones". The number should be output as an int value. Handle numbers with one, two, three or four digits.

Hint: To get the integer value 5 from the character '5' subtract '0', that is '5'-'0'==5.


#include "stdafx.h"
#include <vector>
#include <string>
void pause();
void getNumbers();
void printNumbers();
using namespace std;
vector <int> digits;
char er = 'n'; //for error checking

int main()
{
 //get the numbers and convert them to an int from string
 getNumbers();

 //print the numbers out
 printNumbers();


 //ask for letter to quit the program
 pause();

 return 0;
}


void getNumbers()
{
 er = 'n';
 cout << "Please enter a number with up to 4 digits: ";
 string x;
 cin >> x; 

 //error checking
 for (int i = 0; i < x.size(); ++i)
 {
  for (char n = 'a'; n < 'z'; ++n)
  {
   while (x[i] == n)
   {
    er = 'y';
    cout << "That is not a number. Sorry.\n";
    return;
   }  
  }

  int num = x[i] - '0'; //converts letter to int
  digits.push_back(num); //pushes back into int vector
 }
}

void printNumbers()
{
 //continue to get input while er is equal to y
 while (er == 'y')
  getNumbers();

 for (int i = 0; i < digits.size(); ++i)
  cout << digits[i];

 cout << " is ";

 if (digits.size() == 4)
 {
  if (digits[0] == 1)
   cout << digits[0] << " thousand and ";
  else
   cout << digits[0] << " thousands and ";

  if (digits[1] == 1)
   cout << digits[1] << " hundred and ";
  else
   cout << digits[1] << " hundreds and ";

  if (digits[2] == 1)
   cout << digits[2] << " ten and ";
  else
   cout << digits[2] << " tens and ";

  cout << digits[3] << " ones.\n";
 }


 if (digits.size() == 3)
 {
  if (digits[0] == 1)
   cout << digits[0] << " hundred and ";
  else
   cout << digits[0] << " hundreds and ";

  if (digits[1] == 1)
   cout << digits[1] << " ten and ";
  else
   cout << digits[1] << " tens and ";

  cout << digits[2] << " ones.\n";
 }


 if (digits.size() == 2)
 {
  if (digits[0] == 1)
   cout << digits[0] << " ten and ";
  else
   cout << digits[0] << " tens and ";
  cout << digits[1] << " ones.\n";
 }

 if (digits.size() == 1)
 {
  cout << digits[0] << " ones.\n";
 }

}

void pause()
{
 cout << "\n\nPlease enter a letter: ";
 char key;
 cin >> key;
}


I'm sure there must be a much easier way to print the digits grammatically however I decided to just do this by brute force because this small program doesn't really take up any memory by doing it this way, nor does it slow it down. 

At first when I read this question I was like "what on earth does he want me to do now?" I didn't know if he wanted us to write into a char but then a char can't hold more than single character, so I thought about a vector of chars and pushing them back but that would require at least 5 different chars to push back. So I went with a string and I actually learnt something very useful today.

So string has similar functions as vector, I knew that I could get the size of it by using .size after the variable to return how many characters had been inputted. However on a whim I decided to see what would happen if I took away '0' from the character within the string, as suggested by Bjarne in the book. And it worked, it returned a number which I applied to a interger called num and then pushed that into a vector so printNumbers() had them all in one place.

Also, as a little extra this includes error checking. This doesn't allow any letters and will only allow numbers. I did this by comparing whatever the current character of x was to the alphabet using a for loop. For example, if the string x was equal to k then k would be compared to each letter of the alphabet and if it was equal to one then it returns to main and executes the next function. However the char 'er' has been set to 'y' so it will keep calling getNumbers() until er is equal to 'n'. It took me a while to get this error check working, so I'm pretty proud it does.

Sunday, 27 November 2016

Little Hiatus & Blog Update

This blog is currently on a small hiatus as I focus on university work. I am currently half way through chapter 7 but my assignments are top priority.

I just handed in my first which was Maths & Physics for game play which focused on parabolic trajectories and IK Chains and have a programming one and another maths due by Christmas. Whilst doing my assignments it has made me think about the future of this blog and would love eventually to put up tutorials on maths for games programming and c++ tutorials. 

As someone who has been programming for just under a year, I'd like to keep the topics on this blog simple, especially for beginner programmers. I've found that newbies asking questions in places like stackoverflow or cplusplus are met with patronizing answers from 'experts' whose answers are way too technical for their level of understanding. 

For example, someone had posted a question on an exercise from chapter 5 of P&P on stackoverflow (I was searching for an answer myself and I was stumped) and one user replied with "just use maps". Another user then had to point out that maps aren't introduced till a much later chapter (I'm currently still trying to wrap my head around classes). I guess what I'm trying to say is, I want this site to be a place where newcomers to C++ can learn the basics without the fear of experienced programmers thinking they're stupid. 
     We all learn at different paces and in different ways. I still use system("pause") every now and then despite what every single user on stackoverflow says. I only ever use windows and it works for what I want. Would I use it in a professional program? Of course not, but that doesn't mean I'm idiot for using it. You have to learn to walk before you can run. That's why Bjarne provides you with a header file with functions such as keep_window_open(). That function works in exactly the same way as system("pause") but for the first few chapters I had no idea how it worked, I only knew that it kept the console window open (as visual studio has a habit of closing the console window when the program has finished executing). Now I understand how it works and how to create a function which does the same thing. 

I am in no way an expert, I'm still learning, and have a lot to learn, but this blog was created because I could not find any help on Principles & Practice. There are a few blog posts and Bjarne himself has answered a few questions but there doesn't seem to be anyone who actually goes through their answers and how they got them. Therefore I hope that someone else finds this blog somewhat useful, if not I'm pretty proud of how far I've progressed so far. But enough rambling, thank you to those who are dropping by and if you have any questions I will try to answer as best I can.