Let's do some location arithmetic!

22

1

From the Wikipedia article:

Location arithmetic (Latin arithmeticæ localis) is the additive (non-positional) binary numeral systems, which John Napier explored as a computation technique in his treatise Rabdology (1617), both symbolically and on a chessboard-like grid.

What?

Location numerals is a way of writing numbers using letters of the alphabet.

Binary notation had not yet been standardized, so Napier used what he called location numerals to represent binary numbers. Napier's system uses sign-value notation to represent numbers; it uses successive letters from the English alphabet to represent successive powers of two: a = 2^0 = 1, b = 2^1 = 2, c = 2^2 = 4, d = 2^3 = 8, e = 2^4 = 16 and so on.

An example

ab = 1+2 = 3 in base 10

aabb = 1+1+2+2 = 6 in base 10

Note that aabb can be shortened to bc by replacing any 2 instances of a letter by a higher one.

Addition

You just concatenate the two numbers and simplify.

acd + bde = acdbde = abcdde = acebe = abcf = 39 in base 10

Subtraction

Just remove all digits appearing equally in both parts of the subtraction. Expanding (converting b to aa) may be necessary

abde- ad = be = 18 in base 10

Multiplication

This is a bit harder.

Lets say we want to multiply acd (13) by def (56). First you arrange acd vertically:

a
c
d

Then you add def after the first a:

a def
c
d

Now, c is 2 positions later in the alphabet than a, so we add 2 positions in the alphabet to def to make fgh. That is added to the second row.

a def
c fgh
d

Lastly, d is 1 position later in the alphabet than c, so we add 1 position in the alphabet to fgh to make ghi. That is added to the third row.

a def
c fgh
d ghi

Then you take the sum of the right: def + fgh + ghi = deffgghhi = deggghhi = deghhhi = deghii = deghj (728)

Another example of multiplication

Input:

bc * de

First:

b
c

Then

b ef
c 

Then

b ef
c fg

Note that we wrote down ef on the first line. That's because bc starts with b, and b is the second letter in the alphabet, so we need to shift de by 1 letter, so it becomes ef.

Then

ef+fg

Output:

eh

Division

This is not part of this challenge, because it can get very complex.

Your actual challenge

Your program or function must take input as a string that looks like this:

a + b

And you must output:

ab

Of course, your program or function must support numbers of arbitrary length (up to the string or input limit of your language) with any of the operators +, -, or *. Some more examples:

Input:

ab + bd

Output:

acd

Input:

d - ab

Output:

ac

Input:

ab * cd

Output:

cf

Notes:

  • The order of letters in the output doesn't matter, but you can always assume that the order of letters in numbers in the input will be ascending (a before z).
  • You may take input with a trailing newline and output with a trailing newline.
  • You may not take input as a list of ab, * and bd for ab * bd.
  • The english alphabet is used (abcdefghijklmnopqrstuvwxyz)
  • Your output must be simplified (aa is not allowed, b is required)
  • The input will be simplified (b + c, not aa + bb or aa + aaaa)
  • You may require a space before and the operator (+, -, or *), or you may require there to be none.
  • There will only be one operator per input.
  • You may assume that the output and the input will never go over 2^27-1 (abcdefghijklmnopqrstuvwxyz)
  • This is , so the shortest answer in bytes wins!

programmer5000

Posted 2017-04-10T14:19:12.583

Reputation: 7 828

2d is 2 positions later in the alphabet than c is this wright? shouldn't it be 1? That is added to the second row. on the same sentence, shouldn't it be third? – Felipe Nardi Batista – 2017-04-10T14:28:16.940

1@FelipeNardiBatista the english alphabet is used here, edited that. – programmer5000 – 2017-04-10T14:29:35.000

@programmer5000 still, bc*de==efgh but efgh is 240 not 144 – Felipe Nardi Batista – 2017-04-10T14:52:48.343

1bc*de should be eh – Felipe Nardi Batista – 2017-04-10T14:55:10.810

@Dada there will only be one operator per input. – programmer5000 – 2017-04-10T14:57:53.300

May we use our languages native operators in the input string if they differ from the given +-*? – Jonathan Allan – 2017-04-10T17:50:20.497

@JonathanAllan yes. Out of curiosity, what language uses something other than +-* for addition, subtraction, and multiplication that you would use for golfing? – programmer5000 – 2017-04-10T17:52:26.673

Thanks - I asked because Jelly uses × for multiplication and _ for subtraction, I will post in a second (it would have cost seven bytes to perform the required translations). – Jonathan Allan – 2017-04-10T17:55:32.613

Are we allowed to output an array of characters? – Arnauld – 2017-04-11T09:14:23.707

@Arnauld no, you may not. – programmer5000 – 2017-04-11T11:13:44.173

Answers

3

Jelly, 26 25 bytes

i@€Øað’2*S;ḟ.Ḣ
ḲÇ€VBṚTịØa

Uses Jelly's operators (× rather than * and _ rather than -) in the input string as allowed by the OP.

(Requires spaces around the operators)

Try it online! or see the test suite

How?

i@€Øað’2*S;ḟ.Ḣ - Link 1, transform from input sub-string to value or operator: sub-string
i@€            - 1st index of, for €ach (or 0 if not found) [reversed @rguments] in:
   Øa          -      lowercase alphabet (i.e. a->1, b->2, ..., non-alpha->0)
     ð         - dyadic chain separation i.e. f(result above, substring):
      ’        - decrement (i.e a->0, b->1, ..., non-alpha->-1)
       2*      - 2 raised to that power
         S     - sum
          ;    - concatenate with the substring
           ḟ   - filter out:
            .  -     0.5 (for an operator substring the evaluated 0.5 is removed)
             Ḣ - head (i.e. the evaluation for a location, and the operator otherwise)

ḲÇ€VBṚTịØa - Main link: string                        e.g. 'ab × cd'
Ḳ          - split on spaces                               [['a','b'],['×'],['c','d']]
 Ç€        - last link (1) as a monadic function for €ach  [3,'×',12]
   V       - evaluate as Jelly code                        36
    B      - convert to binary                             [1,0,0,1,0,0]
     Ṛ     - reverse                                       [0,0,1,0,0,1]
      T    - truthy indexes                                [3,6]
       ị   - index into:
        Øa -     lowercase alphabet                        ['c','f'] (i.e. "cf", which is implicitly printed when run as a full program)

Jonathan Allan

Posted 2017-04-10T14:19:12.583

Reputation: 67 804

7

JavaScript (ES6), 136 134 133 bytes

Saved 1 byte thanks to Luke

s=>[...a='abcdefghijklmnopqrstuvwxyz'].filter((c,i)=>eval(s.replace(/\w+/g,s=>[...s].reduce((p,c)=>p|1<<a.search(c),0)))&1<<i).join``

Test cases

let f =

s=>[...a='abcdefghijklmnopqrstuvwxyz'].filter((c,i)=>eval(s.replace(/\w+/g,s=>[...s].reduce((p,c)=>p|1<<a.search(c),0)))&1<<i).join``

console.log(f('ab + bd'));  // acd
console.log(f('d - ab'));   // ac
console.log(f('ab * cd'));  // cf

Arnauld

Posted 2017-04-10T14:19:12.583

Reputation: 111 334

Nicely done! You beat me to it... – programmer5000 – 2017-04-10T14:51:10.317

Does this convert to decimal and back? It appears so. – programmer5000 – 2017-04-10T15:09:53.267

1@programmer5000 Yes indeed. I suspect that many answers will. (Except of course Mathematica, which probably has a built-in for it. ^^) – Arnauld – 2017-04-10T15:15:20.137

It looks like your comment was missing a link. What has a built-in fot it? – programmer5000 – 2017-04-10T15:16:54.150

@programmer5000 (In fact, it was missing a word.) – Arnauld – 2017-04-10T15:20:45.917

How about [...a='abcdefghijklmnopqrstuvwxyz'].reduce(...? – Luke – 2017-04-11T08:27:13.607

@Luke I may have missed an optimization somewhere, but s=>[...a='abcdefghijklmnopqrstuvwxyz'].reduce((p,c,i)=>eval(...)&1<<i?p+c:p,'') is 3 bytes longer. – Arnauld – 2017-04-11T08:37:58.307

True. If you use .filter, you can save 9 bytes: a=>[...d='abcdefghijklmnopqrstuvwxyz'].filter((b,c)=>eval(a.replace(/\w+/g,e=>[...e].reduce((f,g)=>f|1<<d.search(g),0)))&1<<c) – Luke – 2017-04-11T09:10:12.347

@Luke Looks good! I'm waiting for the OP answer to know if we're allowed to return an array. It would still save 1 byte otherwise. – Arnauld – 2017-04-11T10:13:45.060

7

Mathematica, 168 bytes

FixedPoint[StringReplace[x_~~x_:>FromCharacterCode[c@x+1]],Table["a",ToExpression@StringReplace[#,x:LetterCharacter..:>ToString@Tr[2^((c=ToCharacterCode)@x-97)]]]<>""]&

My initial solution (before the post was edited to clarify that the output must be simplified) was 64 bytes shorter:

Table["a",ToExpression@StringReplace[#,x:LetterCharacter..:>ToString@Tr[2^(ToCharacterCode@x-97)]]]<>""

This just modified that solution to work. It's probably shorter to actually use the methods described in the challenge, but I wanted to put this up anyway.

Explanation:

Replaces each sequence of letters with its corresponding integer by character code arithmetic, then converts the resulting string to an expression (which will automatically simplify to an integer), then produces a string of a characters of length equal to that integer, and finally replaces adjacent identical characters with the next character code up until a fixed point is reached.

ngenisis

Posted 2017-04-10T14:19:12.583

Reputation: 4 600

2Oh, there's no 1-char builtin? Suprising! – programmer5000 – 2017-04-10T17:56:22.353

5

Perl 5, 95 bytes

94 bytes of code + -p flag.

s/\w/a x 2**(-97+ord$&)/ge;s/(.*)-\1|\+//;/\*/&&($_=$`x length$');1while s/(.)\1/chr 1+ord$1/e

Try it online!

Three steps here:
- s/\w/a x 2**(-97+ord$&)/ge; converts the input into a string of a only.
- s/(.*)-\1|+//;/*/&&($_=$`x length$') will execute the operator (that are very simple on strings of a): + is the concatenation, - means removing from the first part as many a as there are in the second part, and * means duplicating the first part as many times as there are a in the second part.
- 1while s/(.)\1/chr 1+ord$1/e folds the consecutive same letters into the next letter in the alphabet.

Dada

Posted 2017-04-10T14:19:12.583

Reputation: 8 279

The only answer that doesn't convert to decimal! Nice job! – programmer5000 – 2017-04-10T15:11:42.450

1@programmer5000 Out of 2 answers, I wouldn't call that impressive! – Dada – 2017-04-10T15:12:23.983

5

05AB1E, 29 bytes

ð¡À¬U¦v0yvAyko+}}X.VbRvyiANèJ

Try it online! or as a Test suite

Explanation

ð¡                             # split input on string
  À                            # rotate left
   ¬U¦                         # get the operator, store it in X and remove it from list
      v                        # for each side of the equation
       0                       # push 0 as an accumulator
        yv                     # for each letter in each side of the equation
          Ayk                  # get its index in the alphabet
             o                 # raise 2 to this power
              +                # add to the accumulator
               }}              # end loops
                 X.V           # apply the operator to the 2 numbers now on the stack
                    bR         # convert to binary and reverse
                      v        # for each binary digit
                       yi      # if it is true
                         ANè   # get the letter at that index in the alphabet
                            J  # join stack to a single string

Emigna

Posted 2017-04-10T14:19:12.583

Reputation: 50 798

5

C & x86 asm, 340 Bytes

Compile with -O0

#define G getchar()
g(){int c,a=0;for(;islower(c=G);)a+=1<<(c-97);return a;}
main(){short o[]={[43]=0x4403,[45]=0x442b,[42]=0x6cf7};
mprotect((long)&&l&~4095,4096,7);
for(;;){int c,b=0,a=g();*(short*)&&l=o[G];G;g();asm("xchg %%eax,%0":"+m"(a));
l:asm("addl %1,%%eax":"=a"(c):"m"(a));
for(;a=c>>b;b++)if(a&=1)putchar(97+b);putchar(10);}}

Explanation

Since C doesn't have eval(), I used a table of x86 instructions in its place. I had to choose instructions which were all the same length (or padded with nops), and which expected src and destination of the same types. Of particular annoyance was that MUL can only write to registers, and the 1-byte MUL opcodes can only write to EAX. Additionally, there seemed to be no register-writing SUB instruction which subtracted from memory, instead of the other way around, hence the XCHG.

edit

Since it was asked in the comments, a more traditional appraoch would look like this:

#define G getchar()
#define return r
#define int i
g(){i c,a=0;for(;islower(c=G);)a+=1<<(c-97);r a;}
a(i x,i y){r x+y;}s(i x,i y){r x-y;}m(i x,i y){r x*y;}
main(){i(*o[])(i,i)={[43]=a,[45]=s,[42]=m};
for(;;){i c,b,a=g();b=G;G;g();c=o[b](a,g());
for(b=0;a=c>>b;b++)if(a&=1)putchar(97+b);putchar(10);}}

It's actually a bit shorter, at 301 chars, for a few reasons: 1. Because there need to be a lot of functions, the overhead of each can be chopped with some preprocessor rules. 2. Modern linux protects from execution on the stack, so the mprotect() call to disable this sacrificed 34 bytes. 3. The XCHG call is very sub-optimal, costing another 30 bytes. If not for those things, the x86 combo would win by about 10-20 bytes.

Also chopped 2 bytes from both by improving the islower() call in g.

Dave

Posted 2017-04-10T14:19:12.583

Reputation: 151

I can't really tell how it would compare to a more classical approach in terms of code-size, but I really like your solution. +1 – Arnauld – 2017-04-12T16:21:07.603

5

GNU sed + coreutils, 329 bytes

Yeah, I have no idea what got into me, but at least I know sed scripting a bit better now. Note that this solution requires GNU sed's e extension, which runs a shell command.

/\+/{s/\+//
b S}
/-/{:E
/a+-a+/{s/(a*)(a*)-\2/\1/
b S}
s/.*/echo &|tr b-z- A-Y-/
e
s/([A-Z])/\L\1\1/g
b E}
/\*/{h
:M
/^\*/{x
s/[^\n]*//
s/\n//g
b S}
s/(.).*\*(.*)/echo \2|tr a-z \1-za-z/
e
H
g
s/.(.*)/\1/
h
s/\n.*//
b M}
:S
s/^.*$/echo &|grep -o .|sort|tr -d '\n'/
e
:L
s/(.)\1/\u\1/g
/^[a-z]*$/ q
s/.*/echo &|tr A-Z b-za/;e
b L

I assume that there will not be spaces around the operators. From my terminal:

$ sed -rf golf.sed <<< a+b
ab
$ sed -rf golf.sed <<< ab+bd
acd
$ sed -rf golf.sed <<< abc+b
ad
$ sed -rf golf.sed <<< d-ab
ca
$ sed -rf golf.sed <<< ab*cd
cf
$ sed -rf golf.sed <<< bc*de
eh
$ sed -rf golf.sed <<< acd*def
deghj

And, for those saner than I: the commented version!

#!/bin/sed -rf

/\+/ {
    s/\+//
    b simplify
}

/-/ {
    # expand pattern space; everything will now be 'a's
    :E
    /a+-a+/{
        # Remove doubled 'a's on either side of the dash. For example,
        # for input d-ab, space is now 'aaaa-aaa'; substitute this to 'a'
        s/(a*)(a*)-\2/\1/
        b simplify
    }
    # shift letters that aren't 'a' down and double them
    s/.*/echo &|tr b-z- A-Y-/;e
    s/([A-Z])/\L\1\1/g
    b E
}

/\*/ {
    # Hold space: line 1 is pattern, other lines are output
    h
    :M

    # if space starts with *, we've eaten entire arg0; sum and simplify
    /^\*/ {
        x
        s/[^\n]*//      # remove first line, which is our pattern
        s/\n//g         # remove newlines to add results together
        b simplify
    }

    # convert pattern into shifting command
    s/(.).*\*(.*)/echo \2|tr a-z \1-za-z/

    # execute it, append result to hold space
    e
    H

    # restore pattern, with leading char and all output lines removed
    g
    s/.(.*)/\1/
    h
    s/\n.*//

    b M
}

:simplify
# reorder all letters so all 'a's are before all 'b's are before all 'c's
# are before ... etc    
# See https://stackoverflow.com/questions/2373874
s/^.*$/echo &|grep -o .|sort|tr -d '\n'/
e

:L
# Replace repeated characters with themselves upper-cased, then translate
# upper-cased characters to what they should be.
s/(.)\1/\u\1/g
/^[a-z]*$/ q
s/.*/echo &|tr A-Z b-za/;e
b L

charliegreen

Posted 2017-04-10T14:19:12.583

Reputation: 161

+1 for the sed code and welcome to PPCG! The convention here when not solving in pure GNU sed (or in any other pure language), is to add to the title the system commands used, like "GNU sed + coreutils" for example, even if you mention calling a shell command in description. This is done to differentiate, especially in challenges with leader-boards, from pure GNU sed answers. – seshoumara – 2017-04-12T07:18:19.733

Also, except for the flag 'f' needed every time, any other flag must be counted as 1 byte. So your score is 329. You might want to mention that in description. And for completion, you might think of adding a link to an online sed interpreter, like TIO.

– seshoumara – 2017-04-12T07:30:32.017

To not be all talk and no action, here is a 43 bytes shorter! version of your code (286 bytes including -r), that I found by golfing the commands. I'm sure it can be even shorter.

– seshoumara – 2017-04-12T08:56:44.880

Ah, alright, good to know! Also, nice golfing! What version of sed are you using, though? Yours works in TIO, but in GNU sed 4.4 I just get sed: file golf.sed line 24: ":" lacks a label – charliegreen – 2017-04-13T06:14:11.913

The nameless label is a well known bug in GNU sed, that was fixed in version 4.3. But on PPCG, you can write programs for any sed variant and version, using bugs as features if it helps in golfing. Differences between versions are too small to mention (4.2 vs 4.4), but the variant (standard POSIX sed vs extended GNU sed) needs to be specified in the title, with the mention of system programs called, if any. – seshoumara – 2017-04-13T10:42:25.437

FYI, you are allowed to use the golfing suggestions that people post in comments to update your code, with credits or not depending up to you. It's also very common to show in title the various golfing stages by using the html strike tag, like so: "<s>329</s> 286 bytes". – seshoumara – 2017-04-14T13:09:31.087

4

PHP, 168

Output Ascending with use of eval

[$a,$o,$b]=explode(" ",$argn);function d($s){for(;$i<strlen($s);)$n+=2**(ord($s[$i++])-97);return$n;}for(eval("\$k=d($a)$o d($b);");$i<26;)echo$k&2**$i++?chr(96+$i):"";

PHP, 185 Bytes

Output Ascending

[$a,$o,$b]=explode(" ",$argn);function d($s){for(;$i<strlen($s);)$n+=2**(ord($s[$i++])-97);return$n;}for(;$i<26;)echo(bc.[mul,add,0,sub][ord($o)-42])(d($a),d($b))&2**$i++?chr(96+$i):"";

Online Version

Expanded

[$a,$o,$b]=explode(" ",$argn); # part the input into variables
function d($s){ # make decimal value
    for(;$i<strlen($s);)$n+=2**(ord($s[$i++])-97);
    return$n;
}
for(;$i<26;)
echo(bc.[mul,add,0,sub][ord($o)-42])(d($a),d($b))&2**$i++?chr(96+$i):""; # Bitwise Compare and Output

PHP, 201 Bytes

Output Decending

[$a,$o,$b]=explode(" ",$argn);function d($s){for(;$i<strlen($s);)$n+=2**(ord($s[$i++])-97);return$n;}for($r=(bc.[mul,add,0,sub][ord($o)-42])(d($a),d($b));$r;$r-=2**$l)$t.=chr(97+$l=log($r,2)^0);echo$t;

Online Version

Expanded

[$a,$o,$b]=explode(" ",$argn); # part the input into variables
function d($s){ # make decimal value
    for(;$i<strlen($s);)$n+=2**(ord($s[$i++])-97);
    return$n;
}
for(
$r=(bc.[mul,add,0,sub][ord($o)-42])(d($a),d($b)) # result of the operation
;$r;
$r-=2**$l) # subtract the letter value 
$t.=chr(97+$l=log($r,2)^0); # find greatest letter
echo$t; # Output

Jörg Hülsermann

Posted 2017-04-10T14:19:12.583

Reputation: 13 026

4

Python 3, 176 167 bytes

i=lambda a:str(sum(1<<ord(i)-97for i in a))
def f(a):
 a,b,c=a.split();m=eval(i(a)+b+i(c));r=''
 while m:
  t=0
  while m>=2**t*2:t+=1
  r+=chr(97+t);m-=2**t
 return r

Try it online!

officialaimm

Posted 2017-04-10T14:19:12.583

Reputation: 2 739

1Unless I'm mistaken, you can shave off two bytes by replacing m>=2**(t+1) with m>=2**t*2, and five bytes by replacing a=a.split();m=eval(i(a[0])+a[1]+i(a[2])) with something like b,c,d=a.split();m=eval(i(b)+c+i(d)). – Tutleman – 2017-04-10T21:40:40.017

1Oh, and two more bytes by replacing 2**(ord(i)-97) with 1<<ord(i)-97. – Tutleman – 2017-04-10T21:52:13.820

1I am amazed how readable this solution is compared to other solutions. – Ole Tange – 2017-04-11T12:30:08.133

Thank you :). But i think its also because of python being the language used. The indentation makes the byte count increase, however readable. ;) – officialaimm – 2017-04-11T12:42:45.693

2

PHP, 130

for($d=a;$e=$argn[$i++];)$e!=' '?$d!=b?$$d+=1<<ord($e)-97:$b=$e:++$d;eval("for(;\$j++<27;)echo($a$b$c>>\$j-1)&1?chr(96+\$j):'';");

expanded version:

for($d=a;$e=$argn[$i++];)       // for each char in the input
  $e!=' '?                      //   if space
    $d!=b?                      //     if not the operation
      $$d+=1<<ord($e)-97:       //       add 2^(char - 'a')
      $b=$e:                    //     else save operation
    ++$d;                       //   else increase "pointer"
eval("for(;\$j++<27;)           // for each bit in the output
        echo($a$b$c>>\$j-1)&1?  //   calulate the result and check the bit
          chr(96+\$j):          //     output corrosponding char
          '';                   //     output nothing
     ");

run with php -R <code>.

Christoph

Posted 2017-04-10T14:19:12.583

Reputation: 1 489

1

AWK, 201 bytes

BEGIN{RS="(.)"}n=index(V="abcdefghijklmnopqrstuvwxyz",RT){s+=2^--n}index("+-*",RT){a=s RT
s=0}END{RS="\n"
"(awk '$0="a s"'<<<1)"|getline v
for(j=26;j--;)if((s=v-2^j)>=0){v=s;c=substr(V,j+1,1)c}print c}

"(awk '$0="a s"'<<<1)"|getline v is the best way I could come up with to do an evaluate in AWK. I may be "cheating" a little to call this just AWK, since I am executing a command, but at least the command is also AWK :)

I'm sure I'm missing some way to reduce the byte-count, but I sure can't see it.

Usage is fairly standard, e.g. put the code in FILE and do:

awk -f FILE <<< "bc + ab"

Note that spaces are not required and any non-op/non[a-z] character will be silently ignored. Could be extended to work with numbers greater than "abcdefghijklmnopqrstuvwxyz" by changing the loop. To do division, simply add the / character to the operation string :). Also, will print a blank line if the result <= 0.

Robert Benson

Posted 2017-04-10T14:19:12.583

Reputation: 1 339