Generalized Gematria Calculator

11

3

Create a bidirectional Gematria calculator, for any given sequence of Unicode characters as the alphabet.

Gematri-What?

Gematria is a system of assigning numerical values to symbols, developed by ancient Greeks and adopted by ancient Jews. It is in a way like ASCII or Unicode, it's just non-linear... See the following table (full table is available in the link above):

Index     Letter   Letter name  Value
--------------------------
  0         א         "Alef"     1
  1         ב         "Bet"      2

           ...

  8         ט         "Tet"      9
  9         י         "Yud"      10
 10         כ         "Kaf"      20

           ...

 17         צ        "Tsady"     90
 18         '        "Kuf"       100
 19         ר        "Resh"      200

           ...

The names of letters are not important, only their index in the "Array" of the alphabet and the respective numerical value. The Hebrew alphabet only has 22 letters (not including "final" letters), so the maximum available value is 400.

If we borrow this system to the English alphabet (A-Z) we will end up with A=1, B=2... L=30... U=300... Z=800.

Two things we need to know.

  1. One of the most important features in this system is calculating the "Gematria Value" of a word, by summing up its letters' values. (Some say there is a mystical connection between words, or phrases (when the value of space is zero) - that share the same Gematria Value).

  2. Any non-negative Integer can be represented in symbols. For example (and let's stay with the English alphabet for now) the value of 32 is LB (L=30 + B=2). The value of 1024 is ZTKD (800+200+20+4. Note that ZSSKD is also 1024, but that's not a legal representation, since it can be compacted).

The Challenge

Write a program / a function / a code snippet in your language-of-choice, which is first set up with an alphabet (see API below) and then accept an argument. That argument may be an Integer, or a word/phrase. If it's an Integer - your program should output/return its representation in the alphabet symbols - the most compacted one (see (2) above). If it is a word or a phrase, your program should output/return the Gematria Value (by summing up symbols' values, not counting whitespaces, see (1) above).

API

Your program/function should accept 3 arguments. You can get them from STDIN, or as function arguments, you can even assume they are variables which initialized programmatically prior to your function invocation.

  • First argument - the first character (in Unicode) of the alphabet.
  • Second argument - the last character (in Unicode) of the alphabet.
  • Third argument - An Integer, to be represented in symbols, OR a phrase which is created by the given alphabet.

Output / Return Value: Depending on the third argument, as explained above.

Assumptions

  • First two arguments will always be one character long each, and the second will always be grater than the first.
  • The sequence (first until last, inclusive) will never include any of the values 30-39 (which represent the digits 0-9), otherwise it will make the third argument ambiguous. EDIT: It won't contain space as well, since in phrases spaces are counted as zeros.
  • The third argument, in case it's a phrase, may only contain spaces and letters of the given alphabet. Empty string is not a valid input (you can assume it's not empty). In case it's an Integer, you can assume it's a positive Integer.

Examples

Input                Output

A Z CODE GOLF        175
a s 512              sssssjb
A B 7                BBBA
≐ ⊐ ≤≫ ≥            1700

Scoring

Score = upvotes - length/100.0

Your code should be short, but more-importantly popular. Negative scores can also play along. The winner will be the answer with highest score in a week from now, 2014-11-29 19:20:00 UTC.

Jacob

Posted 2014-11-22T19:18:00.743

Reputation: 1 582

I retagged your question to the catch-all [tag:code-challenge] as I think the scoring is sufficiently different from either code golf or a standard popularity contest. – Martin Ender – 2014-11-22T19:26:31.260

Ok. That's a lot of tags :) thanks. – Jacob – 2014-11-22T19:27:32.683

What is space itself is included in the include list created by the first two characters ? – Optimizer – 2014-11-22T21:10:47.343

Also, what do you mean by the second assumption ? ASCII code for 0 is not 30. – Optimizer – 2014-11-22T21:11:44.747

@Optimizer it is in hex... – feersum – 2014-11-23T07:57:38.190

@Optimizer, you're right. I will add to assumption that space won't be there as well. – Jacob – 2014-11-23T11:36:07.000

The hebrew letter names are a bit incorrect (it is "Tsadik" not "Tsady") (I am a native hebrew speaker) – proud haskeller – 2014-11-23T12:50:29.757

I also feel like adding a column for the actual letters would be nice – proud haskeller – 2014-11-23T12:52:05.000

1@proudhaskeller this is a common mistake since you learn in kindergarten "peh tsady kuf resh" which sounds like tsadik kuf... You are welcome to confirm this with the Academy for Hebrew. – Jacob – 2014-11-23T12:54:15.860

@Jacob Oh. Well, personally I don't really care about the academy. But I am still surprised. I guess it's like over here they sometimes say that native english speakers might have problems with english syntax. – proud haskeller – 2014-11-23T12:56:26.553

@Jacob also I feel that the development of languages shouldn't be governed by some academy - natural progression is the way languages become what they are now. In the far past "Tsady" was named a bit differently, and it is how it should be. – proud haskeller – 2014-11-23T13:03:30.983

Answers

4

CJam, 80 75 70 bytes, Upvotes - 0.7

Arc:Irc\-):N,f#{9,:)f*~}%N<lS-_A,s&{i{1${1$)<},)\,I+o-}h;;}{If-\f=:+}?

Test it here.

This is a full program, which takes input from STDIN and prints the result to STDOUT.

I'm not really sure how I'm supposed to shoot for popularity here, so I'm simply golfing this, hoping to get a reasonably impressive code size instead. ;)

I believe the int-to-string conversion can still be improved, but I don't see it right now.

Thanks to Optimizer for reminding me about set intersection and that empty arrays are falsy.

A                                   "Push integer 10.";
 rc:I                               "Read token, convert to character, save in I.";
     rc                             "Read token, convert to character.";
       \-)                          "Swap, subtract, increment.";
          :N                        "Store number of characters in N.";
            ,                       "Turn into range [0 1 2 ... N-1].";
             f#                     "Map 10^i onto that range.";
               {       }%           "Map this block onto the powers of 10.";
                9,                  "Create range [0 1 2 ... 8].";
                  :)                "Increment each element.";
                    f*              "Multiply each by the current power of 10.";
                      ~             "Unwrap/dump the resulting array.";
                                    "Now we've got the values of the first 9N digits.";
                         N<         "That's too much, so truncate to the first N.";
                           l        "Read the rest of the line.";
                            S-      "Remove spaces.";
                              _     "Duplicate string and get first character.";
                               A,   "Create range [0 1 2 ... 9].";
                                 s  "Turn into string '0123456789'.";
                                  & "Intersection of characters.";

{                      }{        }? "If/else depending on whether the result is empty.";
                                    "If it was a digit...";
 i                                  "Convert string to integer.";
  {                }h               "While that integer isn't zero...";
   1$                               "Copy digit values.";
     {    },                        "Filter digit values.";
      1$                            "Copy remaining integer.";
        )<                          "Increment, compare.";
                                    "This discards values greater than the integer.";
            )\                      "Slice off last digit value, and swap with list.";
              ,I+                   "Get length of list and add to I.";
                 o                  "Print character.";
                  -                 "Subtract digit value from integer.";
                     ;;             "Empty stack.";
                                    "If the string was not a number...";
                         I          "Push initial character.";
                          f-        "Subtract it from each character in string.";
                            \       "Swap differences and digit values.";
                             f=     "Use differences to index into the values.";
                               :+   "Sum up all the values.";

In the second case, the result is left on the stack, which is printed automatically at the end of the program.

Martin Ender

Posted 2014-11-22T19:18:00.743

Reputation: 184 808

5

Java 7, Score = Upvotes - 3.97

Yay!!! Java!!! The world's favourite golfing language in the world. What, you can actually golf in java??? Well, it's kind of like using a bulldozer to putt.

a is expected to contain the first character. b is expected to contain the last character. c is expected to have the input string.

Here is the function golfed:

int d=0;try{d=Integer.parseInt(c);}catch(Exception e){}int l=b-a+1;char[]f=new char[l];int[]g=new int[l];int h=1;int i=1;g[0]=1;f[0]=a;int j;for(j=1;j<b-a+1;){g[j]=(h+=i);f[j]=(char)(f[j++-1]+1);i*=h==10*i?10:1;}if(d==0){h=0;for(char k:c.toCharArray()){for(j=0;j<l;j++){if(f[j]==k){h+=g[j];}}}System.out.println(h);}else{c="";for(j=l;j>0;){if(g[--j]<=d){c+=f[j];d-=g[j++];}}System.out.println(c);}

Here it is indented with structure code:

public class G{

    public static void main(String[] args){
        new G(args);
    }

    public G(String[] args){
        a = args[0].charAt(0);
        b = args[1].charAt(0);
        for (int i = 2; i < args.length; i++){
            c += args[i];
        }
        function();
    }

    char a;

    char b;

    String c = "";

    void function(){
        int d=0;
        try{
            d=Integer.parseInt(c);
        }catch(Exception e){}
        int l=b-a+1;
        char[]f=new char[l];
        int[]g=new int[l];
        int h=1;
        int i=1;
        g[0]=1;
        f[0]=a;
        int j;
        for(j=1;j<b-a+1;){
            g[j]=(h+=i);
            f[j]=(char)(f[j++-1]+1);
            i*=h==10*i?10:1;
        }
        if(d==0){
            h=0;
            for(char k:c.toCharArray()){
                for(j=0;j<l;j++){
                    if(f[j]==k){
                        h+=g[j];
                    }
                }
            }
            System.out.println(h);
        }else{
            c="";
            for(j=l;j>0;){
                if(g[--j]<=d){
                    c+=f[j];
                    d-=g[j++];
                }
            }
            System.out.println(c);
        }
    }
}

Here it is completely expanded:

public class Generator{

    public static void main(String[] args){
        beginning = args[0].charAt(0);
        end = args[1].charAt(0);
        for (int i = 2; i < args.length; i++){
            phrase += args[i];
        }
        function();
    }

    static char beginning;

    static char end;

    static String phrase = "";

    static void function(){
        int convertTo = 0;
        try{
             convertTo = Integer.parseInt(phrase);
        } catch (Exception e){}
        char[] alphabet = new char[end - beginning + 1];
        int[] values = new int[alphabet.length];
        int value = 1;
        int base = 1;
        values[0] = 1;
        alphabet[0] = beginning;
        int i;
        for (i = 1; i < values.length;){
            values[i] = (value += base);
            alphabet[i] = (char)(alphabet[i++-1]+1);
            base*=value==10*base?10:1;
        }
        if(convertTo==0){
            value = 0;
            for (char character : phrase.toCharArray()){
                for (i = 0; i < alphabet.length;i++){
                    if (alphabet[i] == character){
                        value += values[i];
                    }
                }
            }
            System.out.println(value);


        } else {
            phrase = "";
            for (i = values.length;i > 0;){
                if (values[--i] <= convertTo){
                    phrase += alphabet[i];
                    convertTo -= values[i++];
                }
            }
            System.out.println(phrase);

        }
    }
}

TheNumberOne

Posted 2014-11-22T19:18:00.743

Reputation: 10 855

2

APL (upvotes - 1.05)

{U←⎕UCS⋄A←V↑∊(10*0,⍳⌊9÷⍨V←1+|-/U⍺)∘.×⍳9⋄⍬≢0↑⍵:+/A[1+(U⍵~' ')-U⊃⍺]⋄U(U⊃⍺)+{⍵≤0:⍬⋄(¯1+A⍳T),∇⍵-T←⊃⌽A/⍨⍵≥A}⍵}

This is a function that takes the two characters on the left, and the argument to be converted on the right:

      'A' 'Z'{U←⎕UCS⋄A←V↑∊(10*0,⍳⌊9÷⍨V←1+|-/U⍺)∘.×⍳9⋄⍬≢0↑⍵:+/A[1+(U⍵~' ')-U⊃⍺]⋄U(U⊃⍺)+{⍵≤0:⍬⋄(¯1+A⍳T),∇⍵-T←⊃⌽A/⍨⍵≥A}⍵}'CODE GOLF'
175
      gematria←{U←⎕UCS⋄A←V↑∊(10*0,⍳⌊9÷⍨V←1+|-/U⍺)∘.×⍳9⋄⍬≢0↑⍵:+/A[1+(U⍵~' ')-U⊃⍺]⋄U(U⊃⍺)+{⍵≤0:⍬⋄(¯1+A⍳T),∇⍵-T←⊃⌽A/⍨⍵≥A}⍵}
      'A' 'Z' gematria 'CODE GOLF'
175
      'a' 's' gematria 512
sssssjb
      'A' 'B' gematria 7
BBBA
      '≐' '⊐' gematria '≤≫ ≥'
1700

Ungolfed version:

gematria←{
   ⍝ get Unicode values for characters
   first last←⎕UCS¨⍺
   amount←1+last-first
   ⍝ find the value for each character in the alphabet
   alphabet←amount↑∊(10*0,⍳⌊amount÷9)∘.×⍳9

   ⍝ right arg is string: calculate number
   ⍬≢0↑⍵: +/ alphabet[1+(⎕UCS ⍵~' ')-first]

   ⍝ otherwise, right arg is number: find string
   ⎕UCS first+{
      ⍝ number ≤ 0? empty string
      ⍵≤0:⍬

      ⍝ find highest value we can subtract
      val←⊃⌽(⍵≥alphabet)/alphabet

      ⍝ return it, followed by the conversion of the rest of the number
      (¯1+alphabet⍳val), ∇⍵-val
   }⍵
}

marinus

Posted 2014-11-22T19:18:00.743

Reputation: 30 224

2

Haskell, 188 bytes; Upvotes - 1.88

This is a full-blown STDIN-to-STDOUT program, heavily golfed. EDIT: Now in under 200 bytes! EDIT2: Saved one byte with @proudhaskeller's suggestion.

x=[1..9]++map(*10)x
f(a:_:b:_:z@(u:_))|u>'/'&&u<':'=w$read z|1<2=show.sum$map v z where v ' '=0;v c=x!!(length[a..c]-1);w 0="";w n=(\c->c:w(n-v c))$last[d|d<-[a..b],v d<=n]
main=interact$f

It constructs the infinite list of values x = [1,2,3,4,5,6,7,8,9,10,20,30,..] on the first line, and does I/O on the third line. The value of a letter c, given the range [a..b], is then the value at position length [a..c] - 1 of x. On the second line, we branch on the first letter u of the third argument, and either sum its gematria values (if u is not a digit), or greedily construct a word with the given value (if u is a digit).

Ungolfed version with more readable variable names:

values = [1..9] ++ map (*10) values
f (low:_:high:_:rest@(first:_))
  | first > '/' && first < ':' = construct $ read rest
  | otherwise                  = show . sum $ map value rest
  where value ' '   = 0
        value c     = values !! (length [low..c] - 1)
        construct 0 = ""
        construct n = (\c -> c : construct (n - value c)) $
                      last [d | d <- [low..high], value d <= n]
main = interact $ f

Zgarb

Posted 2014-11-22T19:18:00.743

Reputation: 39 083

you could remove the {} from the where clause for a one byte gain – proud haskeller – 2014-12-01T19:35:28.597

1

CJam, 70 bytes, #Upvotes - 0.7

{{_9%)A@9/#*}%}:M;rcrc),\i>:QlS-_@&{Qf#M:+}{i{Q,,M{1$)<},)Q@,=@@-}h;}?

This assumes that a valid input will be passed. Takes input from STDIN like the API spec says and prints result to STDOUT.

Examples:

Input                Output

A Z CODE GOLF        175
a s 512              sssssjb
A B 7                BBBA
≐ ⊐ ≤≫ ≥            1700

Try it online here

Block wise explanation:

{{_9%)A@9/#*}%}:M;
{             }:M;              "Define a function M which takes an input array of"
                                "indeces and calculates the Gematri number for them";
 {          }%                  "Run this code block for each element of the array";
  _9%)                          "Copy the number, take modulus by 9 and increment it";
      A@                        "Put 10 on stack, and rotate to get the number on top";
        9/                      "Integer divide the number by 9";
          #                     "Calculate 10 to the power the above quotient";
           *                    "Multiply the above result by modulus 9";

rcrc),\i>:QlS-_@&
rcrc                            "Read the first two characters, representing the lower"
                                "and upper end of the character list";
    ),                          "Increment the upper end and get a list of U to ASCII 0"
                                "characters where U is the upper limit";
      \i                        "Swap and convert the lower limit to its ASCII number";
        >:Q                     "Slice the character list to get our desired character"
                                "list range and store it in Q";
           lS-                  "Read the rest of the line as string and remove spaces";
              _@&               "Take a copy, get Q on top of stack and take"
                                "intersection with the input string. If the resulting"
                                "string is empty, then the third input was a number";
                 {...}{...}?    "First code block is for string input and second for"
                                "number input based on the above intersected string";

{Qf#M:+}
 Qf#                            "For each character of input string, calculate its"
                                "position in Q";
    M                           "Get the Gematri numbers for these inceces";
     :+                         "Sum them all to get the final Gematri number for the"
                                "input string"

{i{Q,,M{1$)<},)Q@,=@@-}h;}
 i                              "Convert the input number string to integer";
  {                   }h        "Run the code block till we get 0 on top of stack";
   Q,,M                         "Get the first length(Q) Gematri numbers";
       {1$)<},                  "Filter and take only which are less than input number";
              )                 "Pop the last number from the filtered array. This is"
                                "The maximum Gematri number that can be deducted";
               Q@               "Put Q on stack and rotate the remaining filtered array"
                                "to top of stack";
                 ,              "Calculate the length of that array, which is also the"
                                "index of the Gematri number used.";
                  =             "Get the corresponding character to that Gematri number";
                   @@-          "Put the number and Gematri number on top and subtract."
                                "The next loop runs on the above result now";
                        ;       "Pop the resedual 0 from stack. The remaining stack now"
                                "contains just the Gematri characters."

Optimizer

Posted 2014-11-22T19:18:00.743

Reputation: 25 836