Addition in base -1+i

65

12

Gaussian integers are complex numbers of the form a+bi where a and b are both integers. In base -1+i, all Gaussian integers can be uniquely represented using the digits 0 and 1, without the need for a symbol to denote sign.

For instance, 1100 in base -1+i represents the decimal number 2, since

1*(-1+i)^3 + 1*(-1+i)^2 + 0*(-1+i)^1 + 0*(-1+i)^0
= (2+2i) + (-2i) + 0 + 0
= 2

Input will be two Gaussian integers in base -1+i represented using the digits 01. This can take one of the following forms:

  • Two separate digit strings,
  • Two decimal integers consisting of 01 representing the base -1+i numbers (e.g. 1100 for 2 in base -1+i),
  • Two binary integers representing the base -1+i numbers (e.g. decimal 12 or 0b1100 for 2 in base -1+i)
  • A single string separating two digit strings/binary integers by a single non-alphanumeric separator (e.g. 1100 1100 or 12,12 for 2+2)

Output the sum of the two Gaussian integers, also in base -1+i and represented using the digits 01 (in one of the formats allowed as input, not necessarily the same choice). The output is allowed to contain a finite number of leading zeroes.

Your function or program must terminate within 2 seconds for inputs of at most 30 digits each.

Additional clarifications

  • You may assume that the input contains no extraneous leading zeroes. For the special case of 0, you may choose either 0 or the empty string as the representation.

Test cases

0, 0 => 0                                      # 0 + 0 = 0
0, 1 => 1                                      # 0 + 1 = 1
1, 1 => 1100                                   # 1 + 1 = 2
1100, 1100 => 111010000                        # 2 + 2 = 4
1101, 1101 => 111011100                        # 3 + 3 = 6
110111001100, 1110011011100 => 0               # 42 + (-42) = 0
11, 111 => 0                                   # i + (-i) = 0
11, 110 => 11101                               # i + (-1-i) = -1
10101, 11011 => 10010                          # (-3-2i) + (-2+3i) = (-5+i)
1010100101, 111101 => 1110100000100            # (-19+2i) + (3-4i) = (-16-2i)

Longer test cases:

11011011010110101110010001001, 111100010100101001001010010101 => 0
111111111111111111111111111111, 111111111111111111111111111111 => 100100100100100100100100100100
101101110111011101110111011101, 101101110111011101110111011101 => 11101001010001000100010001000100011100
100100010101001101010110101010, 100010011101001011111110101000 => 110000110010101100001100111100010

Sp3000

Posted 2016-03-23T10:43:52.393

Reputation: 58 729

No digit lists? – CalculatorFeline – 2016-03-23T14:48:23.817

@CatsAreFluffy No digit lists, sorry. – Sp3000 – 2016-03-23T15:09:37.067

@CatsAreFluffy: If you really want a list, you could just use an existing answer to repeatedly add 1. – Tim Pederick – 2016-03-23T15:11:44.500

98You can save one byte by changing -1+i to i-1 in the title. – mbomb007 – 2016-03-23T18:11:53.593

1Now we need a conversion the other way around. :P – Rɪᴋᴇʀ – 2016-03-23T18:35:22.513

anybody who even attempts doing this in brainf*ck will get an upvote for all that math they probably did. – tox123 – 2016-03-25T22:18:11.400

3There are 1100 types of people in the world. Those who understand binary, those who don't, those who confuse it with ternary, those who confuse it with base 4, those who confuse it with base 5, those who confuse it with base -1+i, those who confuse it with base 6, those who confuse it with base 7, those who confuse it with base 8, those who confuse it with base 9... – wizzwizz4 – 2016-03-27T09:51:55.713

@wizzwizz4 ..., those who confuse it with base i. – user75200 – 2017-12-28T15:40:56.360

Answers

42

Python 2, 98 97 91 84 bytes

s=input();L=1
for _ in`s`*8:s+=1098*int(str(s).translate('0011'*64));L*=10
print s%L

This does I/O in decimal. The integers have to be separated by the non-alphanumeric character +.

Thanks to @xnor for golfing off 2 bytes!

Try it on Ideone.

How it works

In Arithmetic in Complex Bases, the author shows how to add and multiply complex numbers in bases of the form -n + i.

For base -1 + i, addition is done similarly to regular, binary addition with carry, with two differences:

  • Instead of carrying 1 to the next higher position, we carry 110 to the next three.

  • Carry digits can propagate indefinitely. However, without leading zeroes, the sum a + b has at most eight digits more than the maximum of a and b.

We proceed as follows.

  1. First, we add a and b as if their digits were decimal digits.

    For a = 10101 and b = 11011, this gives 21112.

  2. Next, we form a new number by replacing the digits larger than 1 with a 1, others with a 0.1

    For the sum 21112, this gives 10001.

  3. For each digit larger than 1, we have to subtract 2 from that digit and carry 110 to the next three higher positions. Since 1098 = 10 * 110 - 2, we can achieve this by multiplying the result from step 2 by 1098, then adding that product to the sum.2

    For the sum 21112, this gives 21112 + 1098 * 10001 = 21112 + 10981098 = 11002210.

  4. We repeat steps 2 and 3 a total of d * 8 times, where d is the number of digits of a + b. 3

    For the initial sum 21112, the results are

                          11002210
                          12210010
                        1220010010
                      122000010010
                    12200000010010
                  1220000000010010
                122000000000010010
              12200000000000010010
            1220000000000000010010
          122000000000000000010010
        12200000000000000000010010
      1220000000000000000000010010
    122000000000000000000000010010
                                 .
                                 .
                                 .
    
  5. We take the final sum modulo 10d + 8, discarding all but the last d + 8 digits.

    For the initial sum 21112, the final result is 10010.


1 This is achieved with translate. Repeating the string 0011 64 times makes one repetition line up with the sequence of ASCII characters 0123, achieving the desired replacement.

2 Note that the digits of this sum cannot exceed 3 (initial value 1 plus two 1's from carries).

3 This happens to work for d = 1, and d * 8 > d + 8 otherwise. The code may repeat the steps (d + 1) * 8 times, since s has a trailing L if s is a long integer.

Dennis

Posted 2016-03-23T10:43:52.393

Reputation: 196 637

7This is deep magic. What format is input() expecting? (I get 21112 when I input 10101, 11011.) – Tim Pederick – 2016-03-23T15:20:02.420

1

Never mind; that was running a version translated (unsuccessfully, it seems) to Python 3. It works fine under Python 2.

– Tim Pederick – 2016-03-23T15:28:52.740

9...How. Please. Explain. – Fund Monica's Lawsuit – 2016-03-23T18:50:49.273

@QPaysTaxes I've edited my answer. – Dennis – 2016-03-24T02:12:57.790

@Dennis Now could you explain why that works? For example, why d+8 and not, say, d+9? How???? – Fund Monica's Lawsuit – 2016-03-24T02:15:39.660

@QPaysTaxes I took that from the paper I linked to. I haven't proved it myself. – Dennis – 2016-03-24T02:16:44.307

@Dennis Oh, I see. I'm just reading through it now. – Fund Monica's Lawsuit – 2016-03-24T02:18:38.663

16

Pyth, 34 bytes

_shM.u,%J/eMeN\12-+PMeNm.B6/J2k,kQ

Try it online: Demonstration or Test Suite (takes quite a while). It should satisfy the time restriction though easily, since the online compiler is quite slow in comparison with the normal (offline) compiler.

Explanation:

My algorithm is basically an implementation of addition with carrying. But instead of carrying 1, I have to carry 110 (1100 in base -1+i is the same as 2 in base -1+i). This works mostly fine, but you can get stuck in an infinite loop printing zeros. For instance if you are adding 1 with 11 and currently have the carry 110. So I basically add until I get stuck in a loop and then stop. I think that a loop that a loop will always print zeros and therefore this should be fine.

_shM.u,%J/eMeN\12-+PMeNm.B6/J2k,kQ   implicit: Q = input list of strings
                               ,kQ   create the pair ["", Q]
    .u                               modify the pair N (^) until loop:
      ,                                replace N with a new pair containing:
            eN                           N[1] (the remaining summand)
          eM                             take the last digits of each summand
         /    \1                         count the ones
        J                                store the count in J
       %J       2                        J % 2 (this is the first element of the new pair)
                   PMeN                  remove the last digit of each summand
                  +    m   /J2           and add J / 2 new summand:
                        .B6                 with the value "110" (binary of 6)
                 -            k          remove empty summand
    .u                               returns all intermediate results
  hM                                 extract the digits
 s                                   sum them up to a long string
_                                    reverse

Jakube

Posted 2016-03-23T10:43:52.393

Reputation: 21 462

13

Python 2, 69 67 bytes

f=lambda a,b:a*a+b*b^58and 2*f(a*b%2*6,f(a/2,b/2))|a+b&1if a else b

I/O is done with base 2 integers.

-2 thanks @Dennis.

feersum

Posted 2016-03-23T10:43:52.393

Reputation: 29 566

I take it a*a+b*b^58==0 when a and b are inverses? How does that work? – xnor – 2016-03-23T23:59:57.977

@xnor No, a*a+b*b==58 when one of them is 3 and the other is 7. – feersum – 2016-03-24T00:06:25.383

1It's not obvious me to that (3,7) is the only pair that gives a cycle and needs special-casing. If it is true, then you only actually need to check (a,b)==(3,7) in that order, since (7,3) recurses to (3,7), and maybe there's a shorter expression for that. – xnor – 2016-03-24T00:09:06.730

1Now this is guaranteed to confuse anyone who doesn't know (or forgets) that (a) ^ is XOR, not exponentiation, or (b) XOR has lower precedence than +. – Tim Pederick – 2016-03-24T08:03:29.640

12

Jelly, 29 28 26 24 21 20 bytes

DBḅ1100ḌµDL+8µ¡Dṣ2ṪḌ

This does I/O in decimal. The integers have to be separated by the non-alphanumeric character +.

Try it online! or verify all test cases.

Background

In Arithmetic in Complex Bases, the author shows how to add and multiply complex numbers in bases of the form -n + i.

For base -1 + i, addition is done similarly to regular, binary addition with carry, with two differences:

  • Instead of carrying 1 to the next higher position, we carry 110 to the next three.

  • Carry digits can propagate indefinitely. However, without leading zeroes, the sum a + b has at most eight digits more than the maximum of a and b.

We proceed as follows.

  1. First, we add a and b as if their digits were decimal digits.

    For a = 10101 and b = 11011, this gives 21112.

  2. For each digit larger than 1, we have to subtract 2 from that digit and carry 110 to the next three higher positions. We can achieve this by converting each decimal digit to binary, the resulting binary arrays from base 1100 to integer, and interpret the resulting list of 0's, 1's, 1100's and 1101's as a non-canonical base 10 number.1

    For the sum 21112, this gives 21112 + 1098 * 10001 = 21112 + 10981098 = 11002210.

  3. We repeat steps 2 a total of d + 8 times, where d is the number of digits of a + b.

    For the initial sum 21112, the results are

                          11002210
                          12210010
                        1220010010
                      122000010010
                    12200000010010
                  1220000000010010
                122000000000010010
              12200000000000010010
            1220000000000000010010
          122000000000000000010010
        12200000000000000000010010
      1220000000000000000000010010
    122000000000000000000000010010
    
  4. We discard all but the last d + 8 digits from the final result. This is achieved by discarding everything after the last 2.2

    For the initial sum 21112, the final result is 10010.

How it works

DBḅ1100ḌµDL+8µ¡Dṣ2ṪḌ  Main link. Argument: a + b (implicit sum)

        µ    µ¡       Execute the chain before the first µ n times, where n is
                      the result of executing the chain before the second µ.
         D            Convert a + b to base 10.
          L           Length; count the decimal digits.
           +8         Add 8 to the number of digits.
D                     Convert the initial/previous sum to base 10.
 B                    Convert each digit (0 - 3) to binary.
  ḅ1100               Convert each binary array from base 1100 to integer.
       Ḍ              Interpret the resulting list as a base 10 number.
               D      Convert the final sum to base 10.
                ṣ2    Split at occurrences of 2.
                  Ṫ   Select the last chunk.
                   Ḍ  Convert from base 10 to integer.

1 Note that the digits of this sum cannot exceed 3 (initial value 1 plus two 1's from carries).

2 This works because the last digit that will cancel out cannot be a 3.

Dennis

Posted 2016-03-23T10:43:52.393

Reputation: 196 637

12

Retina, 100 bytes

r+`(.*)(\d|(?!\4))( .*)(.?)
$2$4:$1$3
T` 0
+`1:11(1*:1*)11
:$1
^:*
:::
}`:(1*:1*:)11
1:1$1
(1)*:
$#1

This takes the input separated with a comma. The output always starts with three leading zeroes.

Try it online!

I really wonder if there's a shorter solution for the first stage...

Martin Ender

Posted 2016-03-23T10:43:52.393

Reputation: 184 808

2No, no, the score is perfect as it is ;) – Conor O'Brien – 2016-03-23T18:29:44.887

2Nice score of -2i! – Fund Monica's Lawsuit – 2016-03-23T18:51:21.210

Wow. I did not see this solution when I posted mine... Much more superior than my solution. – Leaky Nun – 2016-04-29T14:07:38.943

@KennyLau I was just looking at it and thinking "hm, I guess I should've added an explanation at some point..." – Martin Ender – 2016-04-29T14:09:32.637

...-2i? This is decimal but the program uses a base that not. – user75200 – 2017-12-28T15:42:41.163

6

Python 3, 289 bytes

This performs digitwise addition from least to most significant digit (in other words, the exact same algorithm you were taught in primary school). The differences are that (a) it's in binary, not decimal, so you carry whenever a digit is 2 or more, and (b) 1 + 1 = 1100, not 10.

Actually, it's also necessary to note that 11 + 111 = 0, or else sums that should become zero will never terminate.

from collections import*
def a(*s,p=0):
 r=defaultdict(int,{0:0})
 for S in s:
  n=0
  for d in S[::-1]:r[n]+=d=='1';n+=1
 while p<=max(r):
  while r[p]>1:
   r[p]-=2
   if r[p+1]>1<=r[p+2]:r[p+1]-=2;r[p+2]-=1
   else:r[p+2]+=1;r[p+3]+=1
  p+=1
 return str([*map(r.get,sorted(r))])[-2::-3]

More golfing is surely possible.

Tim Pederick

Posted 2016-03-23T10:43:52.393

Reputation: 1 411

How certain are you that your "zero detector" is sufficient? – Yakk – 2016-03-23T19:55:51.697

4@Yakk: On a scale of one to peer-reviewed-journal, maybe give it a no-counterexamples-yet? – Tim Pederick – 2016-03-24T07:43:45.830

2

Retina, 157 151 134 133 124 123 bytes

1 byte off thanks to Martin Büttner.

(.+),(.+)
$.1$*0$2,$.2$*0$1,
1
0x
+`(0x*)(,.*)0(x*),
$2,$1$3
{`,

(^|0x0xx0xx)
000
(0x*)(0x*)(0x*0)xx
$1x$2x$3
)`^0+
0
0x
1

Try it online!

Converts to unary, and then repeat the following replacements (shown here in decimal):

122 -> 000
0002 -> 1100 (this can also be 0012 -> 1110 and 1112 -> 2210 or even 2222 -> 3320 or even 3333 -> 4431)

Basically, when larger than two: take away two, add nothing in the previous digit, add one to the previous digit, then add one to the previous digit.

In pseudocode:

if(a[n]>2):
    a[n] -= 2;
    a[n-2] += 1;
    a[n-3] += 1;

Unary implementation:

Each digit (e.g. 3) is shown as the number of xs (e.g. xxx) and then prefixed with 0.

For example, 1234 would be expressed as 0x0xx0xxx0xxxx.

This leaves 0 unchanged, as 101 would be expressed by 0x00x.

Since initially and finally, there is only 0 and 1, the conversion could be easily done by 1->0x and 0x->1.

Click here to see every step.

Leaky Nun

Posted 2016-03-23T10:43:52.393

Reputation: 45 011

1

JavaScript (ES6), 146 126 bytes

r=n=>n&&n%2-r(n>>=1)-i(n)
i=n=>n&&r(n>>=1)-i(n)
g=(x,y,b=(x^y)&1)=>x|y&&b+2*g(b-x+y>>1,b-x-y>>1)
(x,y)=>g(r(x)+r(y),i(x)+i(y))

g converts a Gaussian integer (real and imaginary parts) into base i-1, while r and i converts a base i-1 integer into a Gaussian integer (real and imaginary parts respectively). Once the conversions are in place, I just have to do the arithmetic.

Edit: Saved 20 bytes by calculating the real and imaginary parts separately.

Neil

Posted 2016-03-23T10:43:52.393

Reputation: 95 035

1

C++ 416 bytes, plus #include <vector>\n#include <algorithm>\n (another 40)

using I=int;using v=std::vector<I>;void r(v&x){v r{rbegin(x),rend(x)};x=r;}v a(v L,v R){r(L);r(R);L.resize(std::max(L.size(),R.size()));for(int&r:R)L[&r-R.data()]+=r;while(1){L.resize(L.size()+3);auto it=find(rbegin(L),rend(L),2);if(it==rend(L))break;I i=-1+it.base()-begin(L);i&&L[i+1]&&L[i-1]/2?L[i+1]=L[i]=L[i-1]=0:(++L[i+2],++L[i+3],L[i]=0);}L.erase( std::find(rbegin(L),rend(L),1).base(),end(L));r(L);return L;}

or, with more whitespace:

using I=int;
using v=std::vector<I>;

void r(v&x){v r{rbegin(x),rend(x)};x=r;}
v a(v L,v R) {
  r(L);r(R);
  L.resize(std::max(L.size(),R.size()));
  for(int&r:R)
    L[&r-R.data()]+=r;
  while(1) {
    L.resize(L.size()+3);
    auto it=find(rbegin(L), rend(L), 2);
    if(it==rend(L)) break;
    I i=-1+it.base()-begin(L);
    i&&L[i+1]&&L[i-1]/2?
      L[i+1]=L[i]=L[i-1]=0
    :
      (++L[i+2],++L[i+3],L[i]=0);
  }
  L.erase( std::find(rbegin(L),rend(L),1).base(), end(L));
  r(L);
  return L;
}

Barely golfed. It takes input as a vector of ints, and returns a vector of ints.

Live example.

Yakk

Posted 2016-03-23T10:43:52.393

Reputation: 859