Showing posts with label PHP. Show all posts
Showing posts with label PHP. Show all posts

Wednesday, August 24, 2022

Morse Code Library

Morse code has been on my mind every now and then for the past several years. I don't write as much code as I used to since I left the software development industry, but today I decided it was time to write functions to encode and decode morse.

First, let's define an array to map the encodings for each character.

  1. $morse = array(
  2. " " => "/",
  3. "A" => ".-",
  4. "B" => "-...",
  5. "C" => "-.-.",
  6. "D" => "-..",
  7. "E" => ".",
  8. "F" => "..-.",
  9. "G" => "--.",
  10. "H" => "....",
  11. "I" => "..",
  12. "J" => ".---",
  13. "K" => "-.-",
  14. "L" => ".-..",
  15. "M" => "--",
  16. "N" => "-.",
  17. "O" => "---",
  18. "P" => ".--.",
  19. "Q" => "--.-",
  20. "R" => ".-.",
  21. "S" => "...",
  22. "T" => "-",
  23. "U" => "..-",
  24. "V" => "...-",
  25. "W" => ".--",
  26. "X" => "-..-",
  27. "Y" => "-.--",
  28. "Z" => "--..",
  29. "0" => "-----",
  30. "1" => ".----",
  31. "2" => "..---",
  32. "3" => "...--",
  33. "4" => "....-",
  34. "5" => ".....",
  35. "6" => "-....",
  36. "7" => "--...",
  37. "8" => "---..",
  38. "9" => "----.",
  39. "." => ".-.-.-",
  40. "," => "--..--",
  41. "?" => "..--..",
  42. "'" => ".----.",
  43. "!" => "-.-.--",
  44. "/" => "-..-.",
  45. "(" => "-.--.",
  46. ")" => "-.--.-",
  47. "&" => ".-...",
  48. ":" => "---...",
  49. ";" => "-.-.-.",
  50. "=" => "-...-",
  51. "+" => ".-.-.",
  52. "-" => "-....-",
  53. "_" => "..--.-",
  54. "\"" => ".-..-.",
  55. "$" => "...-..-",
  56. "@" => ".--.-.",
  57. );

This covers almost all possible morse characters, with the exception of accented letters.

Next, we need a function to encode messages.

  1. function morseEncodeMessage($message)
  2. {
  3. global $morse;
  4. $result = "";
  5. for ($i = 0; $i <= strlen($message) - 1; $i++)
  6. {
  7. $character = strtoupper(substr($message, $i, 1));
  8. $result .= (array_key_exists($character, $morse) ? $morse[$character] : "........") . " ";
  9. }
  10. $result = rtrim($result);
  11. return $result;
  12. }
We're looping through the message, one character at a time. Lowercase characters are turned into uppercase characters. We check the global array to see if there's a corresponding key, and if so we get the value associated with that key. If the character can't be found, we convert it to a series of eight dots to indicate that an error has occurred. Each encoded character is separated by a space; an extra trailing space is removed before we return the result. Spaces between words are turned into forward slashes.

Now we need a way of decoding messages.

  1. function morseDecodeMessage($message)
  2. {
  3. global $morse;
  4. $result = "";
  5. $characters = explode(" ", $message);
  6. foreach ($characters as &$character)
  7. {
  8. $result .= in_array($character, $morse, true) ? array_search($character, $morse, true) : "ERROR";
  9. }
  10. unset($character);
  11. return $result;
  12. }
We start by splitting the encoded message into an array, using the spaces between each letter as the separator. Then we loop through the local array and check the global array for each value. If the value is found, we retrieve the key, otherwise we indicate that an error has occurred.

Finally, let's write a unit test to make sure our code is working properly.

  1. function morseTest($message)
  2. {
  3. $testInput = $message;
  4. echo "Input:   " . strtoupper($testInput) . "\n";
  5. $testEncoded = morseEncodeMessage($testInput);
  6. echo "Encoded: " . $testEncoded . "\n";
  7. $testDecoded = morseDecodeMessage($testEncoded);
  8. echo "Decoded: " . $testDecoded . "\n";
  9. echo "Result:  Test " . ($testDecoded == strtoupper($testInput) ? "successful" : "failed") . "!\n";
  10. }
Nothing too complicated. Display the message before encoding, encode the message, display the encoded message, decode the message, display the decoded message, and compare the decoded message with the original.

If we execute the following:
morseTest("Hello world!");

We get this result:
Input:   HELLO WORLD!
Encoded: .... . .-.. .-.. --- / .-- --- .-. .-.. -.. -.-.--
Decoded: HELLO WORLD!
Result:  Test successful!

Sunday, September 5, 2010

Roman Numerals, Part 4

Keith Alexander of Albuquerque, New Mexico writes:
“I like your Roman Numeral library. I needed a function to test for Roman numerals, so I wrote this one.
  1. // Check to see if the string is a Roman Numeral
  2. // NOTE: this doesn't check for fractions, overbars, the Bede "N" (zero) etc.
  3. // NOTE: It also doesn't check for a well-formed Roman Numeral.
  4. function is_roman_numeral( $roman )
  5. {
  6.     // Strip every non-word character
  7.     // - A-Z, 0-9, apostrophe and understcore are what's left
  8.     $roman = preg_replace( "/[^A-Z0-9_']/iu", "", $roman );
  9.     // if it contains anything other than MDCLXVI, then it's not a Roman Numeral
  10.     $result = preg_match( "/[^MDCLXVI]/u", $roman );
  11.     if( $result )
  12.     {
  13.         return FALSE;
  14.     }
  15.     return TRUE;
  16. }

Who knows if blogger is going to show it properly. If not, just contact me and I'll send it you in email or something. Anyway, it's something I wrote in 5 minutes. If you want to add it to your library, modified or otherwise, please feel free.”

Thanks for writing in Keith, and sorry for the late response. There are two ways to validate a Roman number, using regular expressions like you did, and converting back to an Arabic number (if the conversion fails, it's not a Roman number).

I'm not sure about using a regular expression to remove non-word characters. My gut tells me that anything containing such characters should fail validation as a Roman number. Also, I would reverse the match and eliminate the if statement by directly returning the result of the match.

PHP

  1. function isRoman($roman)
  2. {
  3.     return preg_match("/[MDCLXVI]/u", $roman);
  4. }

ASP

  1. function isRoman(roman)
  2.     dim regEx
  3.     set regEx = new RegExp
  4.     with regEx
  5.         .IgnoreCase = true
  6.         .Global = true
  7.         .Pattern = "[MDCLXVI]"
  8.     end with
  9.     if regEx.Test(roman) then
  10.         isRoman = true
  11.     else
  12.         isRoman = false
  13.     end if
  14.     set regEx = nothing
  15. end function


Saturday, May 16, 2009

Chemistry Library

This week's code is probably the biggest single release I've ever made. I considered splitting it up into two or three weeks, but that would be too much work. Before we get started, you'll need my proper case function.


In this function library, we have three major things going on: look up chemical element symbols by atomic number, look up chemical element names by atomic number, and figure out the electron configuration of an atom given the atomic number.


For the chemical element names and symbols, I made an array of all the named elements; currently the highest named element is atomic number 111. There are elements with higher atomic numbers that have been discovered, but not yet named. There is also the possibility that more elements may be discovered in the future. To handle both these situations, I wrote code to generate standardized names if the array lookup fails.


The function(s) for figuring out electron configuration are my favorite. I started with an array of all the subshells in the order they get filled. Then I wrote a function that knows how many electrons can fit in each type of subshell. Finally, I wrote the main function which fills the subshells, taking into account known exceptions to the rules. (e.g. palladium)


Saturday, May 9, 2009

Fuel consumption

Last November I was at a Service Canada office and picked up a pamphlet called Fuel Consumption Guide 2007. Although too old to be relevant (unless you're buying a used car), it contained some formulae for calculating fuel consumption which inspired me to write some code.


This library of functions is among the longest yet, so I won't be posting the code directly on the blog, but here's a list of the functions included:


  • lph() - Calculate fuel consumption rating in litres per 100 kilometres.
  • mpg() - Calculate fuel consumption rating in miles per gallon.
  • lph2mpg() - Convert miles per (imperial) gallon to litres per 100 kilometres.
  • mpg2lph() - Convert litres per 100 kilometres to miles per (imperial) gallon.
  • fuelConsumption() - Calculate fuel consumption in litres.
  • CO2emissions() - Calculate carbon dioxide emissions in kilograms.

Saturday, April 25, 2009

Social Insurance Numbers

Here in Canada, our equivalent of the Social Security Number is called Social Insurance Number. It serves the same purpose and has the same demands for privacy surrounding it.


Once again, I won't be posting the full source code on the blog, only on Snipplr, but I will discuss briefly how it works. It consists of three groups of three digits, and is validated using the Luhn algorithm. I also do a quick regular expression validation to check for numbers which invalidly begin with an eight: ^([1-79]{3})[\-\s]?(\d{3})[\-\s]?(\d{3})$.


Saturday, April 18, 2009

Social Security Numbers

Assuming that you have a legitimate need to capture social security numbers (human resources app?), you may want to validate and format them consistently. The actual code for both languages combined is a little bit too long to post, so I'll just talk about the algorithm.


A social security number is a nine-digit number and can be matched with the following regular expression: ^\d{3}\-?\d{2}\-?\d{4}$. If it can't pass this test, it's not a valid social security number. However, passing this simple test doesn't guarantee validity, so we need to keep checking.



  • No digit group can consist of only zeros, and the first group cannot be 666. We check for these errors with the following regular expression: ((000|666)\-?\d{2}\-?\d{4}|\d{3}\-?00\-?\d{4}|\d{3}\-?\d{2}\-?0000.

  • Numbers from 987-65-4320 to 987-65-4329 are reserved for use in advertisements, and other previously legitimate numbers have been invalidated because of use in advertisments. We check for these errors with the following regular expression: 987\-?65\-?432\d{1}|042\-?10\-?3580|062\-?36\-?0749|078\-?05\-?1120|095\-?07\-?3645|128\-?03\-?6045|135\-?01\-?6629|141\-?18\-?6941|165\-?(16|18|20|22|24)\-?7999|189\-?09\-?2294|212\-?09\-?(7694|9999|219\-?09\-?9999|306\-?30\-?2348|308\-?12\-?5070|468\-?28\-?8779|549\-?24\-?1889)

  • Last but not least, the first three numbers are never higher than 772 (well, not yet; this could change in the future). For this I used a simple string conversion and numeric comparison.


The complete, commented source code is available on Snipplr:


Next week we'll head north of the border to my country and see what the Canadian equivalent of the Social Security Number is, and how we can validate them.

Saturday, April 11, 2009

Homeland Security Advisory System

Back in 2002, a bunch of bureaucrats decided that the United States needed an advisory system so that all federal departments and agencies could communicate what the current threat condition was using the same definitions. Sounds like a good idea to me.


The average citizen can see what the current threat condition is by visiting certain government web sites. The web site for the Department of Homeland Security is a good example.


The Department of Homeland Security also provides a little-known web service which returns the threat condition in XML format so that you can do what you want with it. Let's write a function to retrieve and parse this information down to just the threat condition itself.


ASP



  1. function getThreatLevel()

  2.     dim regEx

  3.     set regEx = new RegExp


  4.     with regEx

  5.         .Pattern = ".*\n.*CONDITION=""(.*)"" />"

  6.         .IgnoreCase = true

  7.         .Global = true

  8.     end with


  9.     dim xmlhttp

  10.     set xmlhttp = Server.CreateObject("Msxml2.ServerXMLHTTP")

  11.     xmlhttp.open "GET", "http://www.dhs.gov/dhspublic/getAdvisoryCondition", "False"

  12.     xmlhttp.send


  13.     getThreatLevel = regEx.replace(xmlhttp.responseText, "$1")


  14.     set xmlhttp = nothing

  15.     set regEx = nothing

  16. end function


As is often the case, the PHP version requires a significantly less amount of code.


PHP



  1. function getThreatLevel()

  2. {

  3.     return eregi_replace('.*CONDITION="(.*)" />', '\1', file_get_contents("http://www.dhs.gov/dhspublic/getAdvisoryCondition"));

  4. }


So now we have a string that contains just the word "ELEVATED" (or whatever the current threat level is at the moment; it's been at elevated for several years at the time of this writing). What can you do with it? Feed it into a switch statement and display an image is one possibility.


Saturday, March 14, 2009

Soundex

This week we're going to write a function to convert letters to their corresponding soundex codes. But wait, there's more! We'll also allow you to pass in an entire string and convert the whole thing. Here's the set up:


  1. function soundex(someString)
  2.     if len(someString) = 1 then
  3.         ' code to convert a single character
  4.     else
  5.         ' loop through the whole string and convert each character
  6.     end if
  7. end function

Inside the loop, we'll recursively call the soundex function to convert the individual character. Actually, this may not perfectly fit the definition of recursion. The only recursive aspect of it is that the function calls itself, but an entirely different execution path is being followed once inside. But that's good, because we won't have the poor performance that sometimes comes with recursion.


Once again, the full source code is a little too long to post, so you'll have to grab it from Snipplr:


Saturday, March 7, 2009

Luhn Algorithm

This week we're writing a function to verify credit card numbers. Credit cards have a check digit which is generated with the Luhn algorithm. The code is too long to post here, but as always it is posted on Snipplr and linked here for your dissemination.


Saturday, February 28, 2009

Phonetic Alphabet

It seems to me that most people have trouble remembering the phonetic alphabet (probably myself included). Unless you're a pilot or air traffic controller, it's unlikely that your job requires you to have it memorized. People get by with whatever word comes to mind ("A as in Adam"), but this seems like an opportunity to write some code.


For maximum reusability, I'm going to write my function to convert individual characters at a time. Looping through the characters of the string will occur outside the function. The function is too long to paste here in its entirety, but it will be posted on Snipplr and linked at the bottom of this entry.


PHP has key/value arrays where we can specify any keys we want, so we'll declare an array containing all the conversions and use the input to retrieve the correct string from the array. ASP's arrays don't have this flexibility; we could achieve this with a Dictionary scripting object, but I'm concerned about the overhead associated with that. I'm going to use a Select...Case statement (AKA switch statement), but there is a chance that this is actually not any better. I'll leave the determination of this as an exercise to the reader. Also left as an exercise to the reader is the writing of the looping code.


Saturday, February 7, 2009

Difference between two stardates

As promised last week, I rejigged my code to calculate the difference between two stardates into a proper function and translated it to ASP. I don't have access to an IIS server right now, so I didn't have a chance to test the ASP version yet, but I'm fairly confident that it works. Let me know if it doesn't!

I won't post the source code directly into this post like I usually do, but as usual it will be available on Snipplr. Expect to see this happen more often in the future. However, I'll talk briefly about what's going on under the hood.

The incoming stardates are just strings, and we split them up based on the official format: first two characters to calculate the years, next three characters to calculate the days, and last character to calculate the hours. The order of the stardates is not important - the code checks which is larger. The same magic numbers from last week are used to get the real values, which are then subtracted. After rounding off any decimal places, the three values are formatted into a human-readable string and returned. It should be very straight-forward.

Saturday, January 31, 2009

Stardate

As promised, today was to be a celebration of one year of Reusable Code. That celebration was overshadowed by a catastrophic hard drive failure early this morning, but luckily I managed to perform a successful recovery with Windows Home Server (after much trial and error).

The function I'm going to share this week is my favorite: calculating the stardate for a given Gregorian calendar date. If you're not familiar with the various Star Trek television series, you might not know what a stardate is. It's a fictional method of measuring time, which is supposed to solve the problems of relativistic time dilation caused by travelling at high velocity. In reality, it progressed at the same rate as time here on Earth.

The method of stardate calculation used here is based on Star Trek: the Next Generation, Star Trek: Deep Space Nine, and Star Trek: Voyager. It does not take the Original Series into account. Also, it is calibrated with the airdates of the television series, not with real world dates. That is to say, if you pass in today's date, it does not return the stardate corresponding to today's date in the 21st century, but today's date in the 24th century Star Trek universe. This is intended to be beneficial for roleplaying games.

ASP

  1. function stardate(someDateTime)
  2.     dim season
  3.     dim episode
  4.     dim fractime
  5.     season = cStr(Year(someDateTime) - 1947)
  6.     episode = str_pad(Round(1000 / 366 * DatePart("y", someDateTime), 0), 3, "0", "left")
  7.     fractime = left(cStr((Hour(someDateTime) * 60 + Minute(someDateTime)) / 144), 1)
  8.     stardate = season & episode & "." & fractime
  9. end function

It is important to note here that for the above function, you will also need my str_pad() function.

PHP

  1. function stardate($timestamp)
  2. {
  3.     $season = date("Y", $timestamp) - 1947;
  4.     $episode = str_pad(round(1000 / 366 * (date("z", $timestamp) + 1)), 3, "0", STR_PAD_LEFT);
  5.     $fractime = substr((date("g", $timestamp) * 60 + date("i", $timestamp)) / 144, 0, 1);
  6.     return $season . $episode . "." . $fractime;
  7. }

I'm somewhat ashamed to admit that there are some "magic" numbers at play here. When I originally wrote this function, I had not intended to make it open source, but events over the past year have led me to the realization that if I did not, it would become lost in the sands of time.

Once upon a time I also wrote some code to calculate the difference between two stardates, but it was only written in PHP. I'll have to port it to ASP and perhaps have it ready for next Saturday's posting, but no promises.


Saturday, January 17, 2009

Triangular Numbers

A triangular number is the sum of the n natural numbers from 1 to n.


ASP

  1. function triangularNumber(someNumber)
  2.     dim i
  3.     dim result
  4.     result = 0
  5.     for i = 1 to someNumber
  6.         result = result + i
  7.     next
  8.     triangularNumber = result
  9. end function

PHP

  1. function triangularNumber($number)
  2. {
  3.     for($i = 1; $i <= $number; $i++)
  4.     {
  5.         $result += $i;
  6.     }
  7.     return $result;
  8. }

Saturday, January 3, 2009

Perfect Numbers

This week's function is for checking if an integer is a perfect number.


ASP

  1. function isPerfect(someNumber)
  2.     dim i
  3.     dim arrFactors
  4.     arrFactors = Array()
  5.     ' Only positive integers can be perfect.
  6.     if someNumber < 1 then
  7.         isPerfect = false
  8.         exit function
  9.     end if
  10.     ' Calculate the factors for the given number.
  11.     for i = 1 to someNumber
  12.         if someNumber mod i = 0 then
  13.             redim preserve arrFactors(UBound(arrFactors) + 1)
  14.             arrFactors(UBound(arrFactors)) = i
  15.         end if
  16.     next
  17.     ' A perfect number is a number that is half the sum of all of its positive divisors (including itself).
  18.     if someNumber = eval(join(arrFactors, " + ")) / 2 then
  19.         isPerfect = true
  20.     else
  21.         isPerfect = false
  22.     end if
  23. end function

PHP

  1. function isPerfect($number)
  2. {
  3.     // Only positive integers can be perfect.
  4.     if ($number < 1)
  5.     {
  6.         return false;
  7.     }
  8.     // Calculate the factors for the given number.
  9.     for($i = 1; $i <= $number; $i++)
  10.     {
  11.         if ($number % $i == 0)
  12.         {
  13.             $arrFactors[] = $i;
  14.         }
  15.     }
  16.     // A perfect number is a number that is half the sum of all of its positive divisors (including itself).
  17.     return ($number == array_sum($arrFactors) / 2) ? true : false;
  18. }

Saturday, December 13, 2008

isPostBack()

Today's function comes not from PHP, but ASP.NET. We're going to replicate the isPostBack() function from ASP.NET's Page object.


ASP

  1. function isPostBack()
  2.     if uCase(Request.ServerVariables("REQUEST_METHOD")) = "POST" then
  3.         isPostBack = true
  4.     else
  5.         isPostBack = false
  6.     end if
  7. end function

PHP

  1. function isPostBack()
  2. {
  3.     return ($_SERVER['REQUEST_METHOD'] == 'POST');
  4. }

Saturday, December 6, 2008

Summation

This week's function is summation using the Gauss method.


ASP

  1. function sum(x, y)
  2.     sum = (x + y) * ((y - x + 1) / 2)
  3. end function

PHP

  1. function sum($x, $y)
  2. {
  3.     return ($x + $y) * (($y - $x + 1) / 2);
  4. }

Saturday, November 22, 2008

Mersenne Numbers

Today we're going to write a function to generate Mersenne numbers. This is useful in searching for Mersenne primes.


ASP

  1. function mersenne(x)
  2.     mersenne = 2^x - 1
  3. end function

PHP

  1. function mersenne($x)
  2. {
  3.     return 2^$x - 1;
  4. }

Saturday, November 8, 2008

Sinc function

Today we're going to a Sinc function, both normalized and unnormalized. Apparently it's useful in digital signal processing.


ASP

  1. Const M_PI = 3.14159265358979323846
  2. ' Unnormalized sinc function.
  3. function sinc(x)
  4.     sinc = sin(x) / x
  5. end function
  6. ' Normalized sinc function.
  7. ' REQUIRES: constant M_PI
  8. function nsinc(x)
  9.     sinc = sin(M_PI * x) / (M_PI * x)
  10. end function

PHP

  1. // Unnormalized sinc function.
  2. function sinc($x)
  3. {
  4.     return sin($x) / $x;
  5. }
  6. // Normalized sinc function.
  7. function nsinc($x)
  8. {
  9.     return sin(M_PI * $x) / (M_PI * $x);
  10. }

Saturday, October 25, 2008

Golden function

The golden function is the upper branch of the hyperbola.


ASP

  1. function gold(x)
  2.     gold = (x + sqr(x^2 + 4)) / 2
  3. end function

PHP

  1. function gold($x)
  2. {
  3.     return ($x + sqrt($x^2 + 4)) / 2;
  4. }

Sorry if this is not exciting stuff. I've got some better stuff coming, but I want to clear out some older stuff that has been waiting a while.


Saturday, October 4, 2008

Nth Root

Both ASP and PHP have a function that allows you to calculate the square root of a number. What if we wanted the cubic root of a number, or some deeper root? Calculating the root of a number is the same as raising that number to a fractional exponent.


ASP

  1. function root(x, y)
  2.     root = x ^ (1 / y)
  3. end function

PHP

  1. function root($x, $y)
  2. {
  3.     return pow($x, 1/$y);
  4. }

x
the number you want the root of
y
the depth you want to go (2 = square, 3 = cubic, etc.)