Skip to content

Testing Algebra

Is \( (x+y)^2 = x^2+y^2 \) true? Or is \( (x+y)^2 = x^2 +2xy+y^2\)? How might we test these algebraic expressions using code?

Using programming to solve algebraic expressions is not a simple process, and we will not tackle it this day. But programming is well-suited for testing algebraic expressions for a range of inputs.

To understand how to test algebraic expressions, we begin by introducing variables and assignment operators. We then explore the notion of equality in JavaScript and how truth can be used. We finish with a discussion of loops, which are used to implement a convenient testing regime for proposed expressions.

Variables

In JavaScript, x = 3; means that x has been assigned the value 3. x will continue to be 3 whenever we use it until we make another assignment for it. For example:

var x = 3;
console.log(x, x + 4); // 3, 7

// use var only once for a variable
x = 5;
console.log(x, x + 4); // 5, 9

Notice the use of var in the code above. When you first use a variable, in this case x, you should use the var keyword to declare it. The reason for this has to do with the scope of the variable, which we explain in more detail in Defining Functions. In jsFiddle, you can click the JSLint button to check for errors in your code. One of the things JSLint will check for are variables that have not been explicitly declared using the var keyword.

Unlike in mathematics, where variable names are extremely terse (e.g., \(x_2\), \(\theta\)), programmers tend to use more descriptive names, particularly when dealing with several variables. A variable’s name can contain letters, underscores, dollar signs, or — besides the first character of the name — numbers. In most cases though, underscores, dollar signs, and even numbers, are avoided.

Spaces are not allowed in variable names. Generally when using multiple words to name a variable, apply camelCase (setting the first word in lowercase, the first letter of each following word in uppercase, and removing spaces to join the words together). Variable names are case sensitive, meaning that an uppercase letter is treated as different from a lowercase letter. Finally, a few special variable names are reserved and cannot be used.

var ABC = 5;
var abc = 7;
var aBc = 3;

// capitalization matters
console.log(ABC, abc, aBc);

// ABc is not defined
// console.log(ABc);

// avoid crazy names like this...
var abc12th_is$ = 3;

// ...even if they're valid!
console.log(abc12th_is$);

// variable names can't start with a number
// var 12x = 3;

// you can declare a variable without a value...
var some;

// ...but this outputs undefined
console.log(some);

// for is a reserved word
// var for = 3;

Understanding Variables

It’s easy to think of variables as the values that are assigned to them. But it’s best to think of variables as simply being names pointing to a separate entity — the value.

A useful analogy is to think of values as phones, whereas variables are phone numbers. In no sense is a phone number confused with a phone. Yet a phone number is used to access a phone, just as we use variables to access particular values. Indeed, without a phone number, there is no way to reach the phone. Generally in JavaScript, a value that is not accessible by a variable ceases to exist.

A phone number and a phone that are linked together need not be linked forever. A phone number may direct to a different phone, or a phone may end up being associated with a different number. In the same way, variables can be reassigned to new values, and values can be assigned to other variables.

Just as the phone number is the way to the phone, the variable is the way to the value.

Assignment Operators

As we indicated before, the assignment operator = assigns the value on its right to the variable on its left. But this is not the only assignment operator available in JavaScript. There are others that combine an operation, such as addition, with the assignment.

Suppose we want to add 3 to x and then store the resulting value back into x. We can achieve this in one of two ways. The obvious method is x = x + 3;. The shortcut method, using an assignment operator, is x += 3;. Both methods are equally valid, but you should reserve the use of latter for simple cases.

var x = 4;
x = x + 3;
console.log(x); // 7

var y = 4;
y += 3;
console.log(y); // 7

+= is the most common assignment operator, but there are others, including -=, *=, /=, and %=. For the full list, see the MDC Assignment Operator page.

For any of the assignment operators, including the fundamental =, JavaScript returns the assigned value as a result of the statement. For example, console.log(x = 3); will assign the value of 3 to x and print this value. This means that you can chain assignments like so: a = b = 3;, where 3 is assigned to b and then the value of b (which is now 3) is assigned to a. If you later assign b to be 5, this does not affect a.

var a, b;

a = b = 3;
console.log(a, b); // 3, 3

b = 5;
console.log(a, b); // 3, 5

The increment ++ and decrement -- operators are commonly used in other programming languages, but should be avoided due to their ambiguous behavior in JavaScript. Much safer alternatives are += 1 and -= 1.

Data Types

JavaScript has a few basic data types: number, string, boolean, object, null, and undefined.

  • A number can be an integer, a decimal, Infinity, -Infinity, and NaN, which stands for “Not a Number”. Operations involving numbers are covered in Coding Arithmetic.
  • A string is any kind of text inside quotes, such as “hello” or ‘bye’. We can concatenate strings: var hb = "hello"+' bye'; assigns the string value “hello bye” to the variable hb. Learn more about strings at MDC.
  • A boolean is a value of true or false. We will see the utility of booleans very soon.
  • An object is a collection of data and behaviors. We will discuss objects in a later post. Note that in JavaScript, functions, arrays, regular expressions, and many other things are objects.
  • null is a placeholder value indicating that a variable has no value.
    undefined is the initial state of a variable that has been declared, but not assigned a value.

So what is a data type? It is not the type of the variable, but rather the type of the data itself. Data types are essential to understanding how JavaScript compares and combines data. In other languages, variables must be declared as a certain type, and using these variables with other types can lead to error messages. JavaScript, on the other hand, converts data to other types as the need arises. No error messages occur, but there can be surprises.

For example, the + operator is overloaded, meaning it performs different operations depending on its operands’ types. When both operands are numbers, + performs addition. When both operands are strings, + performs concatenation, merging the strings together. An interesting thing happens when one argument is a number and the other is a string. Here, JavaScript automatically creates a string from the number, which it then concatenates with the other string. Examples:

console.log(3 + "hi"); // 3hi
console.log("hi" + 3); // hi3
console.log(3 + '1'); // 31

console.log((5 + 3) + '7'); // 87
console.log(5 + 3 + '7'); // 87
console.log('7' + 5 + 3); // 753
console.log('7' + (5 + 3)); // 78

The basic lesson here is that when mixing strings and numbers with +, be sure to use parentheses to specify what needs to be evaluated first.

Why does JavaScript overload + like this anyway? To make it convenient to output calculations:

console.log("My age is " + (3 * 4 + 7) + "!"); // My age is 19!

When mixing data types, JavaScript is more inclined to treat + as an operator for concatenation than addition.

Unlike +, other operators such as - and * are used for a single, dedicated operation. So what happens when you subtract strings? JavaScript tried to create numbers out of the strings to perform a subtraction. If it’s successful, it returns the difference. If it’s unsuccessful, it returns NaN.

You can have JavaScript try to convert strings to numbers in this fashion, but we recommend for clarity that you use the functions parseInt and parseFloat for converting strings to integers and decimals, respectively. See the MDC pages on parseInt and parseFloat for details.

//strings forced to numbers
console.log(4 - "7", 5 + "7" * 3); // -3, 26

//not a number
console.log(4 - "3abc"); // NaN

//explicitly converting strings to numbers
console.log(parseFloat("3")); // 3
console.log(parseInt("3")); // 3
console.log(parseFloat("3.14")); // 3.14
console.log(parseInt("3.14")); // 3
console.log(parseInt("3.57")); // 3
console.log(parseFloat("hi3")); // NaN

//an unhelpful string conversion
console.log("hi" + Math); // hi[object Math]

Truth and Truthiness

When are two quantities equal? If we directly deal with numbers, the answer is rather obvious. But with variables, which can hold — well — various values, it can be useful to determine equality with the operator ===.

Let’s say that x is given the value of 3. Then x === 3 yields the boolean value true, while x === 4 yields false. We also have a “not equal” operator with !==: x !== 4 yields true.

We can also negate with the operator !, so both x !== 4 and !(x===4) yield true.

var x = 3;

console.log(x === 3); // true
console.log(x !== 3); // false
console.log(x !== 4); // true
console.log(!(x === 3)); // false
console.log(!x === false); // true

Those last two expressions, where we negate the boolean evaluation using the exclamation point, tend to be confusing constructs that are best avoided.

Inequality operators are also available: <, >, <=, and >=. For numbers, these work just the same as in mathematics. For strings, comparisons are made by alphabetical order, which can lead to surprises. The following all evaluate to true:

console.log(4 < 13); // true
console.log("a" < "b"); // true

// the next two results might surprise you
console.log("Z" < "a"); // true
console.log("13" < "4"); // true

var x = 3;

console.log(x <= 3); // true
console.log(x < 3); // false
console.log(x < 4); // true
console.log(x > 2); // true
console.log(x >= 2); // true
console.log(x >= 4); // false

So far we have used = for assigning a value to a variable, and === and !== for testing equality. What about == and !=? These are used to compare the truthiness of values: testing if two values are “basically the same.” With these operators, JavaScript attempts to convert values into the same type and then checks to see if they are equal. It can be quite ambitious in its conversions at times.

console.log(3 == "3"); // true
console.log(3 === "3"); // false
console.log(3 !== "3"); // true

console.log(Math); // [object Math]
console.log("[object Math]" == Math); // true
console.log("[object Object]" === Math); // false

Falsy values are values that translate to false in certain boolean contexts. Falsy values include 0, empty strings (i.e., '' and ""), null, undefined, and NaN. All other values are truthy and translate to true — even the string "0", the string "false", empty functions, empty arrays, and empty objects!

When comparing the truthiness of values using == and !=, note that 0 and empty strings are equivalent to each other, null and undefined are only equivalent to themselves, and NaN is not equivalent to anything, including itself! Needless to say, comparing the truthiness of values can be tricky, so in general the strict operators === and !== should be used when making comparisons.

console.log(0 == false); // true
console.log(0 == " "); // true
console.log(0 === " "); // false
console.log(0 === false); // false
console.log(null == false); // false

console.log(NaN == NaN); // false
console.log(null == undefined); // true
console.log(null === undefined); // false
console.log(null === null); // true
console.log(null == 0); // false

var x;
console.log(x === undefined); // true

One can also put together multiple boolean evaluations and combine them: a && b to yield true if both a and b are truthy, a || b to yield true if either a or b are truthy.

//and
console.log(true && true); // true
console.log(true && false); // false
console.log(false && true); // false
console.log(false && false); // false

//or
console.log(true || true); // true
console.log(true || false); // true
console.log(false || true); // true
console.log(false || false); // false

These boolean operators have an interesting feature: they return the last expression they evaluate. So in the case of a && b, JavaScript starts by checking a. If a is false, there’s no need to proceed to b and so false is returned. If a is true, b is checked and its value returned.

In the case of a || b, if a is truthy, then a is returned. Otherwise, the value of b is returned. For example:

var x = 0 && 5;
console.log(x); // 0

var y = 0 || 5;
console.log(y); // 5

var z;
z = z || "bye"; // z is undefined
console.log(z); // bye

z = z || "hi"; // z is truthy so its value is unchanged
console.log(z); // bye

z = 0;
z = z || null; // z is falsy so second operand is assigned even though falsy
console.log(z); // null

For x, 0 evaluates to false and therefore stops since its boolean can already be determined. For y, 0 is again falsy, but this time the program must continue to check if the second operand it true. Thus, y is assigned a value of 5. Notice that neither x nor y have the value true, but rather the value that was returned. The rest of the code sample demonstrates a common usage: assigning a default value when the first term is not defined.

If Statements

When do we use boolean expressions? There are two main cases, with if statements and with loops.

The if statement is designed to conditionally execute one of two blocks of code. It evaluates a condition, executing the first block if the condition is truthy and the second block if it’s falsy.

if(){}else{}

The syntax of the if statement is demonstrated in the following example:

var x = 3;

// the first console.log() is run
if (x <= 4) {
    console.log("less than or equal to 4"); // less than or equal to 4
} else {
    console.log("greater than 4");
}

In this next example, the statement x <= 4<="" code=""> is evaluated as either true or false. Since x evaluates to 3 and is indeed less than or equal to 4, the first console.log() statement is called.

The else block is optional. One can use an if to execute a single block of code:

var x = 3;

// this console.log() statement is skipped
if (x === 4) {
    console.log("Four is here!");
}

console.log("Done");

x = 4;

// this console.log() statement is executed
if (x === 4) {
    console.log("Four is here!");
}

console.log("Done");

It is good practice to use parentheses to clarify the condition, particularly if compound boolean evaluations are used, as in:

var x = 6;

// the second console.log() statement is executed
if ((x <= 4) || (x > 7)) {
    console.log("below or above");
} else {
    console.log("middle");
}

You can nest else if clauses to have additional checks. For example:

var x = 6;

// the second console.log() statement is executed
if (x <= 4) {
    console.log("below");
} else if (x <= 7) {
    console.log("middle");
} else {
    console.log("above");
}

Depending on the number of cases that need to be checked, you might also consider a switch statement (see MDC switch) or an array holding possible outcomes.

For simple if-else statements, the ternary operator is a handy shortcut, returning the value of the first expression if the condition is true and the second expression if the condition is false. For example:

var x = 0;
console.log((x < 4) ? "less" : x + 3); // less

x = 5;
console.log((x < 4) ? "less" : x += 4); // 9

console.log(x) // 9

Loops

There are situations where it would be nice to test a range of values and see what happens in each of the cases. With JavaScript, we can automate that. There are two common types of loops, the for loop and the while loop. There is also a for... in loop for looping over the properties of an object, but that is for another time.

A simple example:

// this loop outputs 0 to 9
for (var i = 0; i < 10; i += 1) {
    console.log(i);
}

console.log("loop done", i); // loop done, 10

This is the anatomy of the for loop:

In the next example, the loop starts with x at 0, incrementing by 1 with each iteration. During the final iteration, x points to 5, but then increments to 6, failing to satisfy the condition and triggering the end of the loop. In the body of the loop, we use the multiplicative assignment operator to change y. We then output both values and compare them using a ternary operator.

var x;
var y = 0.5;

for (x = 0; x < 6; x += 1) {
    y *= x + 1;
    console.log(x, (x < y) ? "less than" : "greater than or equal to", y);
}

console.log(x, y); // 6, 360

The second common type of a loop is a while loop. Let's do a fun example.

var rand = Math.round(Math.random() * 100);
var count = 1;

while ((rand < 40) || (rand >= 60)) {
    console.log(count + ": " + rand);
    count += 1;
    rand = Math.round(Math.random() * 100);
}

console.log(count + ": " + rand);

We use the Math.random() function, scaled and rounded, to produce numbers between 0 and 100. The while loop continues until a number between 40 and 59, inclusive, is produced. It returns the count and the last random integer generated. The classical notion of a while loop is that it is a loop whose number of iterations is unknown as it waits for some condition to be satisfied. A for loop, on the other hand, is one in which the number of iterations should be predictable.

A while loop is structured as follows.

We can replicate the functionality of a for loop using a while loop. For example:

var x = 0;
var y = 0.5;

while (x < 6) {
    y *= x + 1;
    console.log(x, (x < y) ? " less than" : " greater than or equal to", y);
    x += 1;
}

// x iterates one last time, while y is unchanged
console.log(x, y); // 6, 360

It's easy to create a loop that ends up trying to continue forever — an infinite loop. For example:

while(1)  {
    x += 1;
}

increments x by 1 many, many times. In the browser, such code usually generates a message to the user that the script is running for a long time and seeks confirmation to terminate it. While the above is valid code, it is not clickable since we prefer not to make your browsing experience painful.

If you are exposed to enough code, you'll discover that while(1){code} is actually fairly common. Why would someone use this? Well, we can use the statement break; to end such a loop. If we need to, we can use continue; to short-circuit the rest of the code in a block and go to the next iteration. This allows us to perform complex case checking and branching that would be cumbersome to do in a single condition.

To demonstrate this technique, we will do the previous example again.

var x = 0;
var y = 0.5;

while (1) {
    y *= x + 1;
    console.log(x, (x < y) ? "less than" : "greater than or equal to", y);
    x += 1;
    if (x < 6) {
        continue;
    } else {
        break;
    }
}

console.log(x, y); // 6, 360

We could we write the if statement to eliminate the continue, as would automatically happen if no break is encountered. It is generally a good idea, however, to be as expressive as possible about the flow of the program.

And finally, a for loop can be used in that same fashion:

var x = 0;
var y = 0.5;

for (1; 1; 1) {
    y *= x + 1;
    console.log(x, (x < y) ? "less than" : "greater than or equal to", y);
    x += 1;
    if (!(x < 6)) {
        break;
    }
}

console.log(x, y); // 6, 360

Notice here that we used a different if statement for the flow. Which one do you find easier to read?

As we have seen, we can use a variety of loops. But generally, the best practice is to use for loops in simple incrementing situations, while loops with conditions for simple checking, and while(1) loops in complicated looping situations.

A useful pattern to learn is a loop of the form for (a = 0; a < n; a += 1) { }. This loop repeats itself \(n\) times and is useful in following a sequence that includes \(n\) terms. The value of a during the last execution of code is \(n - 1\) while its value after the execution is completed is \(n\).

Our final loop example serves as a warning. You are allowed to modify the loop variable inside a for loop, but it can get hairy and should be avoided.

// modifying the counter in a for loop is generally a bad idea
for (x = 0; x < 10; x += 1) {
    console.log(x);
    x += 2;
}

console.log("after loop, x is " + x);

Testing an Algebraic Expression

And now we are fully in a position to test algebraic expressions. Let's test whether \( (x+y)^2 = x^2+y^2 \) and \( (x+y)^2 = x^2 +2xy+y^2\) are true.

var x, y, xsq, ysq, binsq;

for (x = 0; x < 3; x += 1) {
    xsq = Math.pow(x, 2);
    for (y = 0; y < 3; y += 1) {
        ysq = Math.pow(y, 2);
        binsq = Math.pow(x + y, 2);
        console.log("Is (x+y)^2 = x^2 +y^2 for x=" + x + " and y=" + y + "?", (binsq === xsq + ysq) ? "Yes" : "No");
        console.log(xsq + ysq, binsq);
    }
}

for (x = 0; x < 3; x += 1) {
    xsq = Math.pow(x, 2);
    for (y = 0; y < 3; y += 1) {
        ysq = Math.pow(y, 2);
        binsq = Math.pow(x + y, 2);
        console.log("Is (x+y)^2 = x^2 +2*x*y+y^2 for " + x + " " + y + "?", (binsq === xsq + 2 * x * y + ysq) ? "Yes" : "No");
        console.log(binsq, xsq + 2 * x * y + ysq);
    }
}

The above code loops through a small number of cases, checking and reporting the equality of each. Reporting on each iteration of a loop is a good way to test the code before running a serious trial.

var x, y, xsq, ysq, binsq;

var theyAgree = true;

var numy = 11;
var numx = 11;

for (x = 0; x < numx; x += 1) {
    xsq = Math.pow(x, 2);
    for (y = 0; y < numy; y += 1) {
        ysq = Math.pow(y, 2);
        binsq = Math.pow(x + y, 2);
        if (binsq === xsq + ysq) {
            continue;
        }
        theyAgree = false;
        break;
    }
}

console.log("Case checking suggest " + (theyAgree ? "agreement" : "disagreement"));

theyAgree = true;

for (x = 0; x < numx; x += 1) {
    xsq = Math.pow(x, 2);
    for (y = 0; y < numy; y += 1) {
        ysq = Math.pow(y, 2);
        binsq = Math.pow(x + y, 2);
        if (binsq !== xsq + 2 * x * y + ysq) {
            theyAgree = false;
            break;
        }
    }
}

console.log("Case checking suggest " + (theyAgree ? "agreement" : "disagreement"));

This code is how one would modify the first code sample to do a more realistic check. It tests many more cases than one would want to visually inspect. It also breaks the loop as soon as a disagreement is found. The breaks in the two loops are organized differently for illustrative purposes; both work equally well, but the second is probably more readable to you. For an even more realistic approach, one could use the Math.random() function to test these expressions with arbitrary values.

Based on our tests, our conclusion is that \( (x+y)^2 = x^2+y^2 \) is false while \( (x+y)^2 = x^2 +2xy+y^2\) has a strong likelihood of being true.

Leave a Comment

Leave a Reply