12
\$\begingroup\$

The graph of the modulo operation (\$y = x \mod k\$) looks like this:

Graph of the modulo function

This is a very useful function, as it allows us to create "wrapping" behavior. However, it is very cumbersome when I want to use it to create an appearance of "bouncing" between two walls. The graph of the "bounce" function (\$y = \text{bounce} (x, k)\$) looks like this:

Graph of the "bounce-modulo" function

The period of the graph of \$y = x \mod k\$ is \$k\$. The period of the graph of \$y = \text{bounce} (x, k)\$ is \$2k\$, because it moves upwards for \$k\$ units, and then moves downwards for another \$k\$ units, before returning to where it started. For both functions, the minimum value for \$y\$ is 0, and the maximum is \$k\$ (Actually, for the modulus function with integral inputs, it's \$k-1\$). In addition, for both functions, the value where \$x=0\$ is 0.

The challenge

Given an integer \$x\$ and a positive integer \$k\$, return an integer or floating-point approximation of \$y = \text{bounce} (x, k)\$.

This is , so the shortest valid submission (counted in bytes) wins.

Test Cases

  x,  k -> bounce(x, k)
  0, 14 ->            0
  3,  7 ->            3
 14, 14 ->           14
 15, 14 ->           13
-13, 14 ->           13 (12.999997 etc would be an acceptable answer)
-14, 14 ->           14
191,  8 ->            1
192,  8 ->            0

Bonus points for a Fourier-based approach in Fourier.

\$\endgroup\$
  • \$\begingroup\$ "For both functions, the minimum value for x is 0, and the maximum is k" is just plain wrong. \$\endgroup\$ – Peter Taylor Jul 16 '17 at 5:47
  • \$\begingroup\$ @PeterTaylor Whoops. I means the result. \$\endgroup\$ – Esolanging Fruit Jul 16 '17 at 5:49
  • 1
    \$\begingroup\$ Oops, that's what I thought it said already. It's still wrong. k % k = 0 \$\endgroup\$ – Peter Taylor Jul 16 '17 at 5:51
  • \$\begingroup\$ @PeterTaylor Oh, I understand your question. I had originally designed this with floating-point in mind, then switched to just ints after. Will edit. \$\endgroup\$ – Esolanging Fruit Jul 16 '17 at 6:05
  • 1
    \$\begingroup\$ @PeterTaylor If the arguments are floats, then the maximum is a number arbitrarily close to k. \$\endgroup\$ – Esolanging Fruit Jul 16 '17 at 6:07

16 Answers 16

7
\$\begingroup\$

x86-64 Machine Code, 18 bytes

97
99
31 D0
29 D0
99
F7 FE
29 D6
A8 01
0F 45 D6
92
C3 

This code defines a function in x86-64 machine language that computes bounce(x, k). Following the System V AMD64 calling convention used on Gnu/Unix systems, the x parameter is passed in the EDI register, while the k parameter is passed in the ESI register. As with all x86 calling conventions, the result is returned in the EAX register.

To call this from C, you would prototype it as follows:

int Bounce(int x, int k);

Try it online!

Ungolfed assembly mnemonics:

; Take absolute value of input 'x' (passed in EDI register).
; (Compensates for the fact that IDIV on x86 returns a remainder with the dividend's sign,
; whereas we want 'modulo' behavior---the result should be positive.)
xchg   eax, edi      ; swap EDI and EAX (put 'x' in EAX)
cdq                  ; sign-extend EAX to EDX:EAX, effectively putting sign bit in EDX
xor    eax, edx      ; EAX ^= EDX
sub    eax, edx      ; EAX -= EDX

; Divide EDX:EAX by 'k' (passed in ESI register).
; The quotient will be in EAX, and the remainder will be in EDX.
; (We know that EAX is positive here, so we'd normally just zero EDX before division,
; but XOR is 2 bytes whereas CDQ is 1 byte, so it wins out.)
cdq
idiv   esi

; Pre-emptively subtract the remainder (EDX) from 'k' (ESI),
; leaving result in ESI. We'll either use this below, or ignore it.
sub    esi, edx

; Test the LSB of the quotient to see if it is an even number (i.e., divisible by 2).
; If not (quotient is odd), then we want to use ESI, so put it in EDX.
; Otherwise (quotient is even), leave EDX alone.
test   al, 1
cmovnz edx, esi

; Finally, swap EDX and EAX to get the return value in EAX.
xchg   eax, edx
ret

Note that the first section (that takes the absolute value) could have equivalently been written:

; Alternative implementation of absolute value
xchg    eax, edi
neg     eax
cmovl   eax, edi

which is the exact same number of bytes (6). The performance should be similar, perhaps slightly faster (except on certain Intel chips, where conditional moves are slow).

XCHG is, of course, relatively slow and would not be preferred over MOV except in code golfing (that the former is 1-byte when one of the operands is the accumulator, whereas a register-register MOV is always 2 bytes).

\$\endgroup\$
6
\$\begingroup\$

Jelly, 3 bytes

æ%A

Try it online!

Built-ins ftw.

Explanation

æ% is a useful built-in here. I don't know how to describe it, so I'll just provide the output for some inputs:

As x goes from 0 to infinity, xæ%4 goes 0,1,2,3,4,(-3,-2,-1,0,1,2,3,4,) where the part in parentheses is repeated to infinity both ways.

\$\endgroup\$
5
\$\begingroup\$

Python 2, 29 27 bytes

lambda x,k:abs(x%k-x/k%2*k)

Try it online!

\$\endgroup\$
3
\$\begingroup\$

Python 3, 27 bytes

lambda x,k:k-abs(k-x%(k*2))

Try it online!

\$\endgroup\$
3
\$\begingroup\$

Ruby, 40 bytes 32 bytes

b=->(x,k){(x/k+1)%2>0?x%k:k-x%k}

Try it online!

Explanation

Hi, this is my first answer on this site! This code is based on the observation that the bounce function behaves exactly like modulo when (n-1)k <= x < nk and n is odd, and behaves like a reversed modulo operation when n is even. (x/k+1) is the smallest integer greater than x/k (which is x/k+1 rounded down to an integer). Therefore, (x/k+1) finds the n mentioned above. %2>0 checks to see if n is odd or even. If n mod 2 > 0, then n is odd. If n mod 2 = 0, then n is even. If n is odd, then the bounce function should equal x mod k. If n is even, the bounce function should be the reverse, equal to k - x mod k. The whole expression (x/k+1)%2>0?x%k:k-x%k finds n, then executes the x mod k if it is odd, and executes k - x mod k otherwise.

The answer was improved based on a suggestion from Cyoce.

\$\endgroup\$
  • \$\begingroup\$ You can convert this to a lambda. Instead of def b(x,k) ... end use ->x,k{...} \$\endgroup\$ – Cyoce Jul 20 '17 at 7:23
  • \$\begingroup\$ And since you're dealing with integers, .to_i isn't necessary. \$\endgroup\$ – Cyoce Jul 20 '17 at 7:25
2
\$\begingroup\$

Mathematica, 19 bytes

Abs@Mod[#,2#2,-#2]&
\$\endgroup\$
2
\$\begingroup\$

Pyth, 5 bytes

aa%Ey

Verify all testcases.

Fork of my Python answer.

\$\endgroup\$
1
\$\begingroup\$

J, 25 bytes

Hint:

This is just regular modulo on the ladder numbers. For example, in the case of 5: 0 1 2 3 4 5 4 3 2 1

Here is a (not yet well-golfed) solution in J. Will try to improve tomorrow:

[ ((|~ #) { ]) (i.@>:,}:@i.@-) @ ]

compressed: [((|~#){])(i.@>:,}:@i.@-)@]

compressed2: [((|~#){])(<:|.|@}.@i:)@]

Try it online!

\$\endgroup\$
  • \$\begingroup\$ I feel like i: can be used here, but I haven't tried a solution yet \$\endgroup\$ – Conor O'Brien Jul 16 '17 at 16:46
  • \$\begingroup\$ @ConorO'Brien check out my compressed2 version, it saves a few bytes using i:. Just haven't had time to update the main one and provide an explanation. I expect an expert could shave off another 4 or 5 bytes at least... \$\endgroup\$ – Jonah Jul 16 '17 at 16:48
  • \$\begingroup\$ ((|~#){])]-|@}:@i: for 18 bytes \$\endgroup\$ – miles Jul 16 '17 at 22:20
  • \$\begingroup\$ @miles beautiful, tyvm \$\endgroup\$ – Jonah Jul 16 '17 at 22:28
1
\$\begingroup\$

QBIC, 25 30 27 bytes

g=abs(:%:)~a'\`b%2|?b-g\?g

Did a bit of restructuring...

Explanation

g=abs(   )  let g be the absolute value of 
       %    the (regular) modulo between
      : :   input a read from cmd line, and input b read from cmd line
~a \ b%2    IF the int division of A and B mod 2 (ie parity test) yields ODD
  ' `         (int divisions need to be passed to QBasic as code literals, or ELSE...)
|?b-g       THEN print bouncy mod
\?g         ELSE print regular mod
\$\endgroup\$
  • \$\begingroup\$ Does QBIC do something different for MOD operations than other Basic implementations do? Other Basics return MOD with the same sign as the dividend; that would fail when x is -13 and k is 14. \$\endgroup\$ – Cody Gray Jul 16 '17 at 9:18
  • \$\begingroup\$ @CodyGray Nope, it gave -13. Fixed now. \$\endgroup\$ – steenbergh Jul 16 '17 at 9:25
  • \$\begingroup\$ Don't you need abs both times? \$\endgroup\$ – Neil Jul 16 '17 at 9:31
  • \$\begingroup\$ @Neil do you have a testcase for that? \$\endgroup\$ – steenbergh Jul 16 '17 at 9:33
  • \$\begingroup\$ @Neil nvm, I've fixed it by restructuring the whole thing. \$\endgroup\$ – steenbergh Jul 16 '17 at 9:43
1
\$\begingroup\$

C89, 40 bytes

t;f(x,k){t=abs(x%k);return x/k%2?k-t:t;}

A C port of my x86 machine code answer, this defines a function, f, that calculates the bounce-modulo for the parameters x and k.

It uses C89's implicit-int rule, so that both parameters, the global variable t, and the function's return value are all implicitly of type int. The global variable t is just used to hold a temporary value, which ends up saving bytes, compared to repeating the computation on either side of the conditional operator.

The abs function (absolute value) is provided in the <stdlib.h> header, but we don't have to include it here, again thanks to C89's implicit-int rule (where the function is implicitly declared and assumed to return int).

Try it online!

Ungolfed version:

#include <stdlib.h>

int Bounce(int x, int k)
{
    int mod = abs(x % k);
    return (x/k % 2) ? k-mod : mod;
}

Looking at this in light of my hand-tuned machine code, compilers actually generate pretty good output for this. I mean, they should; it's a pretty simple function to optimize! I did uncover a minor bug in GCC's x86-64 optimizer, though, where it curiously produces larger code when you tell it to optimize for size and smaller code when you tell it to optimize for speed.

\$\endgroup\$
  • \$\begingroup\$ m;f(x,k){m=abs(x%k);x=x/k%2?k-m:m;} is shorter \$\endgroup\$ – Cows quack Jul 16 '17 at 18:12
  • \$\begingroup\$ Except that it doesn't actually return a value, @cows, other than under certain ill-defined circumstances due to a quirk of the GCC code generator on x86 targets. It's a template I see people use here, but it doesn't work for me, any more than pulling random garbage off the stack that just happens to be the correct answer. \$\endgroup\$ – Cody Gray Jul 17 '17 at 5:07
1
\$\begingroup\$

Haskell, 37 Bytes

Try it online!

(!)=mod;x#k|odd$x`div`k=k-x!k|1<2=x!k

How to use:
Call as 15#14 for non-negative left arguments and as (-13)#14 for negative left arguments, because Haskell would interpret -13#14 as -(13#14) if you are using something like ghci. The TIO-link simply takes two command-line arguments.

Explanation:
First redefines the binary infix operator ! to be the same as mod. Haskell's mod always outputs a non-negative value, so we don't need the abs that other solutions here do. It then checks whether x/k (integer division) is odd and if so, returns k-x mod k (ie the back-bounce) or else it returns x mod k.

\$\endgroup\$
  • \$\begingroup\$ This is probably just a matter of taste, but I personally prefer not defining ! since it doesn't save any bytes over x#k|odd$x`div`k=k-x`mod`k|1<2=x`mod`k \$\endgroup\$ – Mark S. Jul 20 '17 at 3:32
1
\$\begingroup\$

PHP, 40 50 bytes

damn dollars. damn import overhead. :)

integer version:

[,$x,$k]=$argv;$y=abs($x)%$k;echo$x/$k&1?$k-$y:$y;

or

[,$x,$k]=$argv;echo[$y=abs($x)%$k,$k-$y][$x/$k&1];

float version, 56 bytes:

Replace abs($x)%$k with fmod(abs($x),$k).


edit: fixed results for negative x

\$\endgroup\$
  • 4
    \$\begingroup\$ "Damn dollars". Yeah, money stinks... \$\endgroup\$ – steenbergh Jul 16 '17 at 9:44
  • 2
    \$\begingroup\$ How about €argv or £argv? Those would look nice :x \$\endgroup\$ – Ismael Miguel Jul 16 '17 at 12:12
1
\$\begingroup\$

JavaScript (ES6), 36 32 bytes

k=>f=x=>x<0?f(-x):x>k?k-f(k-x):x

Recursively bounces x against 0 and k, so very much in the spirit of the challenge.

\$\endgroup\$
0
\$\begingroup\$

Common Lisp, 41 bytes

(lambda(x k)(- k(abs(-(mod x(* k 2))k))))

Try it online!

\$\endgroup\$
0
\$\begingroup\$

C (gcc), 43 53 bytes

Edit: Fixed negative issue

int f(int x,int y){return x/y%2?abs(y-x%y):abs(x%y);}

Try it Online!

\$\endgroup\$
  • 2
    \$\begingroup\$ This provides the wrong answer for (-13, 14) (-13 instead of 13). The modulus and remainder operations behave different on negative numbers. \$\endgroup\$ – CAD97 Jul 16 '17 at 7:30
0
\$\begingroup\$

R, 28 bytes

pryr::f(abs((x-k)%%(2*k)-k))

Which evaluates to the function:

function (k, x) 
abs((x - k)%%(2 * k) - k)

Which appears to be the method that most solutions use. I didn't look at them before making this.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.