Gematria Converter

Coding Critique is the place to post source code for peer review by other members of DevNetwork. Any kind of code can be posted. Code posted does not have to be limited to PHP. All members are invited to contribute constructive criticism with the goal of improving the code. Posted code should include some background information about it and what areas you specifically would like help with.

Popular code excerpts may be moved to "Code Snippets" by the moderators.

Moderator: General Moderators

Post Reply
veleshanas
Forum Newbie
Posts: 10
Joined: Sat May 24, 2008 10:58 pm

Gematria Converter

Post by veleshanas »

Hello forum,

I have just finished writing a PHP/HTML page that converts Arabic numbers into Hebrew numerals (each of 22 Hebrew alphabets having a numerical value) and vice versa. I present the entire PHP file below.

The program works in the following steps:
1. Input through Textbox "in" is matched against two regex formula (thanks, GeertDD).
2. If the input is an integer from 1 to 999, it is processed by the num2heb logic.
3. If the input solely consists of Hebrew characters, it is processed by the heb2num logic.
4. The num2heb logic converts a number into a Hebrew numeral, or a string of Hebrew letters, the sum of whose values equalling to the input.
5. The heb2num logic calculates the numerical value of a Hebrew string.
6. The results are printed out.

I am wondering if there is any way to improve num2heb. I think I am using too many interceding variables to get the final result. Does any one know a simpler and more elegant way to do what I did?

a. The first bunch of if-clauses determines $_900, the Hebrew numeral representation of the hundreds of the input.
b. The tens and units part is extracted from the original input ($_in099) and thrown into the second bunch of if-clauses, which returns the Hebrew numeral of the tens ($_090).
c. The units part ($_in009) of the input is thrown into the third bunch of if-clauses and converted into the Hebrew numeral of the units ($_009).
d. The fourth if-bunch creates the Hebrew representation for the tens and units ($_099) out of concatenation of $_090 and $_009. Exceptional creation of $_099 for 15 and 16 is written in the same section.
e. The results of Processes a. and d. are concatenated as the final outcome; $_999.


:wink: veleshanas

Code: Select all

<HTML>
 
<HEAD>
<TITLE>GemConverter<TITLE/>
<META docOwner = "veleshanas" DATE = "2008-04-08">
<META REVISION = "added hb2num logic" DATE = "2008-05-25">
<META writtenFor ="PHP Version 5.2.4-2ubuntu5">
<META HTTP-EQUIV = "Content-Type" CONTENT = "text/html; charset=utf-8">
 
</HEAD>
 
<BODY>
 
GemConverter returns:<BR />
&nbsp;&nbsp;&nbsp;a. Hebrew numerals for integers from 1 to 999<BR />
&nbsp;&nbsp;&nbsp;b. Numerical values for any length of Hebrew strings
<BR /><BR />
 
<FORM ACTION = "GemConverter.php" METHOD = "post">
<INPUT TYPE = "text" NAME = "in" SIZE = 12>
<INPUT TYPE = "submit" VALUE = "Convert">
</FORM>
 
<HR />
 
<?php
 
$in = NULL;
$in_hb = NULL;
$num = NULL;
 
 
 
// Validates input with Regex.
 
if($_POST["in"] == NULL){
    print "Enter a number or a Hebrew string.";
}elseif(preg_match("/^[1-9][0-9]{0,2}$/",$_POST["in"])){
    $in = $_POST["in"];
}elseif(preg_match("/^[\p{Hebrew} ??]+$/u",$_POST["in"])){
    $in_hb = $_POST["in"];
}else{
    die("illegal input");
}
 
 
 
// Here starts the num2hb logic.
 
if($in <= 999 and $in >= 900){$_900='???';}
elseif($in <= 899 and $in >= 800){$_900 = '??';}
elseif($in <= 799 and $in >= 700){$_900 = '??';}
elseif($in <= 699 and $in >= 600){$_900 = '??';}
elseif($in <= 599 and $in >= 500){$_900 = '??';}
elseif($in <= 499 and $in >= 400){$_900 = '?';}
elseif($in <= 399 and $in >= 300){$_900 = '?';}
elseif($in <= 299 and $in >= 200){$_900 = '?';}
elseif($in <= 199 and $in >= 100){$_900 = '?';}
else{$_900='';}
 
 
 
$_in099 = substr($in,-2);
 
if($_in099 <= 99 and $_in099 >= 90){$_090 = '?';}
elseif($_in099 <= 89 and $_in099 >= 80){$_090 = '?';}
elseif($_in099 <= 79 and $_in099 >= 70){$_090 = '?';}
elseif($_in099 <= 69 and $_in099 >= 60){$_090 = '?';}
elseif($_in099 <= 59 and $_in099 >= 50){$_090 = '?';}
elseif($_in099 <= 49 and $_in099 >= 40){$_090 = '?';}
elseif($_in099 <= 39 and $_in099 >= 30){$_090 = '?';}
elseif($_in099 <= 29 and $_in099 >= 20){$_090 = '?';}
elseif($_in099 <= 19 and $_in099 >= 10){$_090 = '?';}
else{$_090 = '';}
 
 
 
$_in009 = substr($in,-1);
 
if($_in009 == 9){$_009 = '?';}
elseif($_in009 == 8){$_009 = '?';}
elseif($_in009 == 7){$_009 = '?';}
elseif($_in009 == 6){$_009 = '?';}
elseif($_in009 == 5){$_009 = '?';}
elseif($_in009 == 4){$_009 = '?';}
elseif($_in009 == 3){$_009 = '?';}
elseif($_in009 == 2){$_009 = '?';}
elseif($_in009 == 1){$_009 = '?';}
else{$_009 = '';}
 
 
 
if($_in099 == 16){$_099 = '??';}
elseif($_in099 == 15){$_099 = '??';}
else {$_099 = $_090.$_009;}
 
 
 
$_999 = $_900.$_099;
 
if(strlen($_999) > 2){
    $_999g = substr($_999, 0, strlen($_999) -2).'?'.substr($_999, -2);}
else{
    $_999g = NULL;}
 
 
 
// Here starts the hb2num logic.
 
$count = substr_count($in_hb, '?');
$mult = 1 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 2 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 3 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 4 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 5 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 6 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 7 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 8 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 9 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 10 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 20 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 20 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 30 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 40 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 40 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 50 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 50 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 60 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 70 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 80 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 80 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 90 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 90 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 100 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 200 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 300 * $count;
$num = $num + $mult;
 
$count = substr_count($in_hb, '?');
$mult = 400 * $count;
$num = $num + $mult;
 
 
 
// Prints out the results.
 
if(is_null($in) == 0){
    print 'The Hebrew numeral for '.number_format($in).' is:<BR />'.$_999.'<br />'.$_999g;
}elseif(is_null($in_hb) == 0){
    print 'The numerical value for the Hebrew string '.$in_hb.' is '.$num.'.';
}else{
    print "";
}
 
?>
 
</BODY>
 
</HTML>
dml
Forum Contributor
Posts: 133
Joined: Sat Jan 26, 2008 2:20 pm

Re: Gematria Converter

Post by dml »

You have blocks of code repeated multiple times with only numbers changing. It's the kind of thing one often sees in calculations with tax brackets: this is much more interesting. When you see that kind of repetition, it's a good idea to put the numbers into a data structure which you then loop over: it takes less space, it avoids copy-paste errors, and it means that the data structure can be used for conversions both ways.

Code: Select all

 
// replace Hebrew letters here, can't get them to work in my browser
$letter_values = array(400=>'a', 300=>'b', ..., 2=>'y', 1=>'z') 
 
The conversion, if I understand it correctly, is a matter of looping through and chopping off the biggest letter value you can every time, so you start with say 764, chop off 400 (appending the letter to the result every time you chop its numerical value off) leaving 364, chop off 300 leaving 64, chop off 60 leaving 4, chop off 4, leaving nothing so you're done.
FrostbyteX
Forum Newbie
Posts: 3
Joined: Wed May 28, 2008 3:19 pm

Re: Gematria Converter

Post by FrostbyteX »

Couldn't you use something like this...?

Untested (at work) but I think the idea is right.

Code: Select all

 
<?php
 
$vals = array(400 => '?',  300 => '?', 200 => '?',100 => '?', 90 => '?', 80 => '?', 70 => '?', 60 => '?', 50 => '?', 40 => '?', 30 => '?', 20 => '?', 10 => '?', 9 => '?', 8 => '?', 7 => '?', 6 => '?', 5 => '?', 4 => '?', 3 => '?', 2 => '?', 1 => '?');
$keys = array_keys($vals);
 
function num2heb($num)
{
    $retval = "";
    for($i = 0; $i < 22; $i++)
    {
        $key = $keys[$i];
 
        while($num > $key)
        {
            $retval .= $vals[$key];
            $num -= $key;
        }
    }
    return $retval;
}
?>
 
veleshanas
Forum Newbie
Posts: 10
Joined: Sat May 24, 2008 10:58 pm

Re: Gematria Converter

Post by veleshanas »

Hello dml and FrostbyteX,

Wow, arrays can work wonderful tricks! It goes to show I have a loooot of learning to do about programming.


With the way FrostbyteX coded the algorithm, it seems as if no Hebrew numerals above 500 is necessary. This is really good. However, I cannot seem to get the code working. I expected that the following code returns «aabcd» for «831» but the program gets timed out on Line 16.

What is the problem?? :cry:

Code: Select all

<?php
 
 
$vals = array(400 => 'a', 20 => 'b', 10 => 'c', 1 => 'd');
$keys = array_keys($vals);
 
function num2heb($num)
{
    $retval = "";
    for($i = 0; $i < 4; $i++)
    {
        $key = $keys[$i];
 
        while($num > $key)
        {
            $retval .= $vals[$key];
            $num -= $key;
        }
    }
    return $retval;
}
 
$num = 831;
print num2heb($num);
 
?>
dml
Forum Contributor
Posts: 133
Joined: Sat Jan 26, 2008 2:20 pm

Re: Gematria Converter

Post by dml »

I can't see immediately what it is, but if it timed out, there's probably an infinite loop there: as FrostbyteX said, it's a sketched out solution that wasn't tested.

Print out the values of $num and $key on each loop iteration to see if they're what you expected. Maybe do some checks like throwing an error if they're not numeric.
veleshanas
Forum Newbie
Posts: 10
Joined: Sat May 24, 2008 10:58 pm

Re: Gematria Converter

Post by veleshanas »

Hello dml,

I've found that the WHILE clause is an infinite loop. Because "print" command cannot print out $key, I'd imagine that detracting that variable from $num never changes the value of $num.

After a very long struggle in front of my PC screen, I got the function work in the following construction.
*It is actually another function that converts Hebrew numerals to numbers. I could not resisting to try a new thing. ;)
**Hebrew letters are replaced by Latin alphabets for simplification.

This suggests that user defined functions cannot read variables outside the function brackets. Or is there any trick that makes it possible? It would be slightly more convenient because the $vals array is used in two functions for my program. [And there are two more similar arrays that are needed by one function only.]

Code: Select all

function heb2num($heb){
 
$vals = array(400 => 'v', 300 => 'u', 200 => 't', 100 => 's', 90 => 'r', 80 => 'q', 70 => 'p', 60 => 'o', 50 => 'n', 40 => 'm', 30 => 'l', 20 => 'k', 10 => 'j', 9 => 'i', 8 => 'h', 7 => 'g', 6 => 'f', 5 => 'e', 4 => 'd', 3 => 'c', 2 => 'b', 1 => 'a');
        foreach($vals as $value => $numeral){
        $num += $value * substr_count($heb, $numeral);
        $heb = str_replace($numeral, '', $heb);
    }
return $num;
}
dml
Forum Contributor
Posts: 133
Joined: Sat Jan 26, 2008 2:20 pm

Re: Gematria Converter

Post by dml »

Code: Select all

 
$hebrew_number_mapping = array(...);
function num2heb($num){
  global $hebrew_number_mapping;
  foreach($hebrew_number_mapping as $num=>$letter){
    //...
  }
}
 
The 'global' declaration first thing in the function allows you to use variables not declared inside the function. Since the variable might be referred to from non-adjacent code, it's a good idea to give it a more meaningful name than $vals.

It should be mentioned that there are a lot of ways of misusing global variables. There are good reasons for using one here, but I'd do some research before using them in other contexts.
veleshanas
Forum Newbie
Posts: 10
Joined: Sat May 24, 2008 10:58 pm

Re: Gematria Converter

Post by veleshanas »

Thanks! Works perfect. :)

I have just read a few pages of O'Reilly's intro book about PHP and MySQL, where I found a good explanation about defining functions, variable scopes and globals. Everything I needed to know was in the first 40 or so pages. Sigh, I am not very good at systematic learning....
Post Reply