# Undulant numbers [duplicate]

An undulant number is a number where its digits alternate between up and down like the following number: 461902 or 708143, or even 1010101, but not 123, because 2 < 3.

Write a program or function which returns a truthy value if a number is undulant, and a falsy value otherwise. The shortest code wins.

Note: Single digit numbers are a valid input but are not considered udulant, thus isUndulant returns false for n < 10.

• Number input as string, integer, float...? Jul 11, 2011 at 19:07
• What's the objective here? Code-golf (shortest function)? Jul 11, 2011 at 20:22
• @JBernardo: I would think True or undefined behavior, as it would be a better base case for recursion. Jul 11, 2011 at 21:26
• Your definition of undulant number is not in agreement with the standard definition: mathworld.wolfram.com/UndulatingNumber.html. Is this intentional? Jul 14, 2011 at 12:03
• My solution could be 16% smaller if the base case were true (as would make sense IMHO). Jul 15, 2011 at 23:22

(pdf)eTeX, 129 chars

\def\a#1#2{\if#2?\ifx\r\s\def\s{1}\else
True\end\fi\fi\edef\t{\pdfstrcmp{#2}{#1}}\ifx\s\t
False\end\fi\let\s\t\a#2}\expandafter\a


Compiling with pdfetex filename.tex 1324? gives a pdf output. TeX is primarily a typesetting language, and outputting instead to stdout would take around 20 more chars. Also the strange requirement for one-digit numbers (false rather than true) takes me 26 chars.

## J, 45

*./(n>9),(}:(=-)}.)(}:*@-}.)n#:~10$~>.10^.n=.  Sample use:  *./(n>9),(}:(=-)}.)(}:*@-}.)n#:~10$~>.10^.n=. 461902
1
*./(n>9),(}:(=-)}.)(}:*@-}.)n#:~10$~>.10^.n=. 708143 1 *./(n>9),(}:(=-)}.)(}:*@-}.)n#:~10$~>.10^.n=. 1010101
1
*./(n>9),(}:(=-)}.)(}:*@-}.)n#:~10$~>.10^.n=. 123 0 *./(n>9),(}:(=-)}.)(}:*@-}.)n#:~10$~>.10^.n=. 5
0


I'm pretty sure there's a finer way of twisting Insert / to do more of the work in a go, but I've been J-less for months, I need to get back to it.

• It will be hard to beat J in this problem. nice solution! Jul 13, 2011 at 15:21

### Ruby, 72 70 characters

Q=10;k=->n,v{(n%Q-n/Q%Q)*v<0?k[n/Q,-v]:n<Q};u=->n{n>9&&k[n,-1]|k[n,1]}


Usage and testcases:

p u   # <= true
p u  # <= true
p u    # <= false
p u    # <= false
p u     # <= false


Single digits yield false:

p u       # <= false


Consecutive identical digits also return false:

p u      # <= false
p u    # <= false


# J, 30 bytes

*/0<(#,]*{.*1 _1$~#)2-/\a.i.":  A different approach than the other J answers.  */0<(#,]*{.*1 _1$~#)2-/\a.i.":461902
1
*/0<(#,]*{.*1 _1$~#)2-/\a.i.":708143 1 */0<(#,]*{.*1 _1$~#)2-/\a.i.":1010101
1
*/0<(#,]*{.*1 _1$~#)2-/\a.i.":123 0 */0<(#,]*{.*1 _1$~#)(}.-}:)a.i.":5
0


Would be 3 characters shorter if 5 were considered undulant.

• Well, at least I can console myself with the thought that I had the lead for an hour. :-) Mar 24, 2012 at 20:58

z=tail>>=zipWith compare
q[]=0>1
q s=all(/=EQ)$s++z s u=q.z.show  This requires the commonly used language pragma (or -X flag): NoMonomorphismRestriction. If you don't admit that, we have to add 4 characters and define z thus: z s=zipWith compare s$tail s

• I am so furious that you beat my 104 char solution. u n=length s>1&&(a[GT,LT]||a[LT,GT])where s=show n;z=q compare s$tail s;w=q(==)z;q=zipWith;a=and.w.cycle It's kinda elegant. zipWith once with compare like you did, then zipWith again with (==) and cycle[GT,LT] or cycle[LT,GT] as second arg. Jul 15, 2011 at 23:07 • you could inline w as tail>>=zipWith compare which would shorten few bytes. Dec 27, 2014 at 12:20 • also, I golfed a shorter version of q: q[]=0<1;q(a:b:s)|a/=b,a/=EQ=q$b:s;q _=0>1 Dec 27, 2014 at 12:28
• actually, here's an even shorter version: q s=and$all(/=EQ)s:zipWith(/=)s(tail s) Dec 27, 2014 at 12:31 • @proudhaskeller - neither version passes all tests. They both fail on 3 (should be False), and the first fails many others, like 32 and 101010101. Dec 29, 2014 at 7:18 # Sage, 83 76 bytes f=lambda x:uniq(cmp(*x[i-2:i][::(-1)^i])for i in[2..len(x)])in[,[-1]]  Got the idea to use cmp(*[..]) from JBernardo. In Sage, uniq(...) is an alias for list(set(...)). Edit: just noticed that for x < 10, uniq(cmp(...)) == [], which isn't on [,[-1]]. If x were input as a string, instead of an integer, I could get another 4 characters out! • I had an idea to use sum(uniq(...))^2, since sum([1,-1]) = 0, and the sums of the singletons  and [-1] squares to 1. Unfortunately, it fails on a thrice-repeated digit; 1011101. Jul 12, 2011 at 16:58 • Nice. I should learn sage. BTW, I just realized that backticks will append an L if the number is bigger than 2**32 in Python and affects the result. Does that happens on Sage? Jul 12, 2011 at 20:02 • Yeah, Sage makes a few things nice for golfing... for instance, its ridiculous startup time is spent importing a huge tree of modules. The Sage Integer class doesn't bother with the L because Sage is preparsed python; 1234 -> Integer('1234'). You can jump right into using Sage here: sagenb.org Jul 12, 2011 at 20:29 ### Python: 101 100 characters Before minification: undulate = (lambda n: n > 9 and all(cmp(*digits) == (i % 2) * 2 - 1 for i, digits in enumerate(zip(min(n,n[1:]), max(n,n[1:])))))  After minification: a=lambda b:b>9and all(cmp(*c)==d%2*2-1 for d,c in enumerate(zip(min(b,b[1:]),max(b,b[1:]))))  ## Python, 134 129 chars def f(x):d=[cmp(*i)for i in zip(x,x[1:])]if x>9 else;n=d>0;return all(i<0 for i in d[n::2])&all(i>0 for i in d[n<1::2])  Ungolfed: def f(x): if x>9: d = [cmp(*i)for i in zip(x,x[1:])] #difference of x[i] and x[i+1] else: d =  #trick to return False if x<10 using less chars n = d>0 #First digit is -1 or 1? neg = d[n::2] #negative numbers if x is Undulant pos = d[not n::2] #positive numbers if x is Undulant #check if all negs are -1 and all pos are 1 and return value return all(i<0 for i in neg) and all(i>0 for i in pos)  ## JavaScript, 88 chars function _(i){i+='';c=i;f=i[a=x=1];for(g=f<c;d=i[x++];c=d)g^=a&=g?d<c:d>c;return!f^a}  In essence, turn the number into a string and compare adjacent characters, flipping the expectation for each. • In JavaScript, a function doesn't need a name and the question asks explicitly for a function, so you can save two characters. – Ry- Jul 13, 2011 at 14:34 # K, 41 bytes {(x>9)&~max(=). 1_'-':'1_'(<':;>':)@\:$x}


E.g.

{(x>9)&~max(=). 1_'-':'1_'(<':;>':)@\:$x}1212130659 1b  # CoffeeScript, 9867 53 bytes (n)->0!in((n[i]>=c^(n<n)+i)%2for c,i in n[1..])  Tests: [ '01010101' # true '12345' # false '1010101' # true '887685' # false '9120734' # true '090909' # true ]  Uncompressed: undulant = (n) -> direction = n < n return n.split('').every (cur, i) -> prev = arr[i-1] or 10 * direction +(prev >= cur) is (direction+i)%2  # J, 443936 31 bytes */2(0<#@],0>*/\)*2-/\".;' ',.":  Usage as before. I hadn't noticed that my last edit made the inequality with 0 check completely unnecessary. :-) Previous answer (+ explanation): (0=+/2=/\u)*(1<#u)**/2~:/\2<:/\u=.".;' ',.":  Usage:  (0=+/2=/\u)*(1<#u)**/2~:/\2<:/\u=.".;' ',.":461902 1  The answer has four parts: 1. u=.".;' ',.": This reads in the number as a string ":, splits it into a list of characters preceded by spaces ' ',., stitches it back together ;, converts it back to numbers ". and then stores the result u=. This basically turns 461902 into 4 6 1 9 0 2 which I find easier to process in J. 2. */2~:/\2<:/\ This operates on the value stored in u. It takes each pair of characters and checks if the left one is less than or equal to the right one 2<:/\ so 4 6 1 9 0 2 becomes 1 0 1 0 1. It then takes the result of this and checks each pair of numbers for inequality 2~:/\ so 1 0 1 0 1 becomes 1 1 1 1. Finally it multiplies them all together to get either a 0 or a 1 */ At this point we could return the answer if it weren't for 2 things: a single digit returns 1 when the question requires a 0; and equal numbers are treated the same as 'less than' so 461900 returns 1 instead of 0. Bummer. On we go... 3. (1<#u) This checks if the number of items stored in u #u is greater than 1 and returns false if it's just a single digit number. 4. (0=+/2=/\u) This takes each pair of numbers stored in u and checks for equality 2=/\u. It then sums the answers and checks if it has 0. The results of parts 2, 3 and 4 are then multiplied together to (hopefully) produce a 1 when the number meets the requirements specified in the question. • Nice job re-taking the lead, but I just borrowed a trick from yours! Mar 25, 2012 at 17:56 • (That being said, I think you could take my a.i.": to shave a few more characters off.) Mar 25, 2012 at 18:03 • Unfortunately, I'm probably going to have to put that inequality check back in - my answer fails now for 11, 22, 33, 44 etc. Mar 25, 2012 at 19:57 # Haskell, 82 bytes c=cycle[(<),(>)] l!n=n>9&&and(zipWith3($)l(show n)$tail$show n)
u n=c!n||((>):c)!n


Try it online!

• I count only 83 characters in this solution. (Are you on Windows, perhaps? Write the file with unix line endings, which is legal Haskell.) Jul 16, 2011 at 16:00
• Thanks, I was using 'wc' to count my characters on Cygwin. I count 82 characters. I used the following code, as wc seems to be outputting an extra character. (Vim doesn't show a trailing newline, but notepad does...) readFile "Undulant.hs" >>= print . length . dropWhile (== '\n') . reverse . filter (/= '\r') Jul 18, 2011 at 23:11
• c=cycle[(<),(>)] can be shortened to c=(<):(>):c. Feb 1, 2018 at 14:19
• zipWith3($)l(show n)$tail$show n can be zipWith3($)l=<<tail$show n and ((>):c) can be tail c. All together 70 bytes: Try it online! Feb 1, 2018 at 14:25 # Python, 119 108 bytes def u(x):l=[cmp(i,j)for i,j in zip(x,x[1:])];print x>9and all([i*j<0 for i,j in zip(l,l[1:])])and l!=  • Nice use of xor. You can cut quite a few characters out with ... for a,b in zip(t,t[1:]) rather than using ranges. Also, you don't need the brackets in all([...]) -- Python makes a generator when it finds (... for ...), even if the parentheses are for a function call. Jul 14, 2011 at 7:50 • Thank you very much for your advice! They have been very valuable! -20 chars Jul 15, 2011 at 8:12 • Very nice solution. Few more characters x>9 and all(i^j for i,j in zip(l,l[1:])) and remove if l else False. – Ante Jul 16, 2011 at 17:09 • It is not working in all cases. Two cases are problematic: only 2 digits (e.g. 11), and last 2 digits are same and larger than one before (e.g. 12155). First problem is since there is no testing if x<100. Second is because 'one way comparison'. It can be fix with cmp(i,j) and instead i^j set i*j<0, and testing and l!=0. Few more characters :-/ – Ante Jul 16, 2011 at 17:24 • Hmmm... print saves one character over return, but is it legitimate? The spec does ask for a function that "returns". – user2186 Jul 17, 2011 at 17:03 ## Python, 155 chars g=lambda a,b:all(x>y for x,y in zip(a,b)) u=lambda D:g(D[::2],D[1::2])&g(D[2::2],D[1::2]) def U(n):D=map(int,str(n));return(n>9)&(u(D)|u([-d for d in D]))  # C++, 94 chars bool u(int N){int K,P,Q,U=1,D=1;while(N>9)P=N%10,Q=(N/=10)%10,K=D,D=U&Q<P,U=K&Q>P;return U^D;}  same method as my Erlang awnser with a for loop rather than recursion. ## Python 105101 100 chars c=lambda r,t:len(r)<2 or(cmp(*r[:2])==t and c(r[1:],-t)) u=lambda x:x>9and c(x,cmp(*x[:2])or 1)  Recursive solution. c(r,t) checks if first char of r is less (t==-1) or greater (t==1) of second char, and call opposite check on shortened string. • Nice. You can save a character in the first line by removing 0, and you can save three characters on the second line by writing u=lambda x:x>9 and c(x,cmp(*x[:2])or 1) – user2186 Jul 17, 2011 at 17:14 • Tnx. I didn't like any() from the beginning :-) – Ante Jul 17, 2011 at 20:31 • You can save one more by writing x>9and. – user2186 Jul 17, 2011 at 21:35 # Perl/re, 139 bytes Doing everything in regex is kind of a bad idea. /^(?:(.)(?{local$a=$1}))?(?:(?>((.)(?(?{$a lt$3})(?{local$a=$3})|(?!)))((.)(?(?{$a gt$5})(?{local$a=$5})|(?!))))*(?2)?)(?(?{pos>1})|(?!))$/


I'm using Perl 5.12 but I think this will work on Perl 5.10. Pretty sure 5.8 is out though.

for (qw(461902 708143 1010101 123 5)) {
print "$_ is " . (/crazy regex goes here/ ? '' : 'not ') . "undulant\n"; } 461902 is undulant 708143 is undulant 1010101 is undulant 123 is not undulant 5 is not undulant  # GolfScript, 48 bytes [..,(<\1>]zip{..$=\-1%.$=-}%(\{.@*0<*}/abs  Hoping to beat J, my first time using GolfScript. Didn't quite succeed. # JavaScript, 666562 60 bytes Takes input as a string, returns true for undulant numbers, an empty string (falsey) for single digit numbers and false otherwise. ([s,...a])=>a+a&&a.every(x=>eval(s+"<>"[++y%2]+x,s=x),y=s<a)  ## Try it Run the Snippet below to test 0-9 and 25 random numbers <10,000,000. f= ([s,...a])=>a+a&&a.every(x=>eval(s+"<>"[++y%2]+x,s=x),y=s<a) tests=new Set([...Array(10).keys()]) while(tests.add(Math.random()*1e7|0).size<35); o.innerText=[...tests].map(x=>(x=x+).padStart(7)+ = +JSON.stringify(f(x))).join\n <pre id=o></pre> ## Explanation A few fun little tricks in this one so I think it warrants a rare explanation to a JS solution from me. ()=>  We start, simply, with an anonymous function which takes the integer string as an argument when called. [s,...a]  That argument is immediately destructured into 2 parameters: s being the first character in the string and a being an array containing the remaining characters (e.g. "461902" becomes s="4" and a=["6","1","9","0","2"]). a+a&&  First, we concatenate a with itself, which casts both occurrences to strings. If the input is a single digit number then a will be empty and, therefore, become and empty string; an empty string plus an empty string is still an empty string and, because that's falsey in JS, we stop processing at the logical AND and output our empty string. In all other cases a+a will be truthy and so we continue on to the next part of the function. a.every(x=>)  We'll be checking if every element x in a returns true when passed through a function. y=s<a  This determines what our first comparison will be (< or >) and then we'll alternate from there. We check if the string s is less than the array a, which gets cast to a string in the process so, if s is less than the first character in a, y will be true or false if it's not. s+"<>"[++y%2]+x  We build a string with the current value of s at the beginning and x at the end. In between, we index into the string "<>" by incrementing y, casting its initial boolean value to an integer, and modulo by 2, giving us 0 or 1. eval()  Eval that string. s=x  Finally, we pass a second argument to eval, which it ignores, and use it to set the value of s to the current value of x for the next iteration. ## PowerShell, 88 Naïve and trivial. I will golf later. filter u{-join([char[]]"$_"|%{if($n){[Math]::Sign($n-$_)+1}$n=$_})-notmatch'1|22|00|^$'}


# JavaScript, 112

function(n,d,l,c,f){while(l=n%10,n=n/10|0)d=n%10,c?c>0?d>=l?(f=0):(c=-c):d<=l?(f=0):(c=-c):(c=d-l,f=1);return f}


You only need to pass it one argument. I could probably golf this further with a for loop.

• (d>=l -> d>0) and (d<=l -> d<2) perhaps? I'm not looking closely, as perhaps d might contain fractional parts that might skew it. Jul 18, 2011 at 23:16
• @trinithis: That's a lowercase L, not a 1. Thanks though!
– Ry-
Jul 18, 2011 at 23:54
• Where's DejaVu Sans Mono or Bitstream Vera Sans Mono when you need it? Perhaps I need to customize stackoverflow with some custom css or a user script... Jul 19, 2011 at 0:46
• @trinithis: I agree, the font choice isn't that great. Bolding doesn't stand out enough...
– Ry-
Jul 19, 2011 at 4:07

# Erlang, 137123 118 chars

u(N)->Q=N div 10,u(Q,N rem 10,Q>0,Q>0). u(0,_,D,U)->D or U;u(N,P,D,U)->Q=N rem 10,u(N div 10,Q,U and(Q<P),D and(Q>P)).

• Won't this return True so long as there has been at least one up and one down transition anywhere? Won't it return True for, say 1234321? Jul 12, 2011 at 2:36
• @ MtnViewMark, yeah it did thanks, I misunderstood the question fixed now hopefully. Jul 12, 2011 at 3:33

# CJam, 30 bytes

CJam is newer than this challenge, so this does not compete for the green checkmark, but it's not a winner anyway (although I'm sure this can actually be golfed quite a bit).

l"_1=\+{_@-\}*;]"_8'*t+~{W>},!


Test it here.

## How it works

Firstly, I'm doing some string manipulation (followed by eval) to save 5 bytes on duplicate code:

"..."_8'*t+~
"..."        "Push this string.":
_       "Duplicate.";
8'*t   "Replace the 8th character (the -) with *.";
+~ "Concatenate the strings and evaluate.";


So in effect my code is

l_1=\+{_@-\}*;]_1=\+{_@*\}*;]{W>},!


First, here is how I deal with the weird special case of a single digit. I copy the digit at index 1 and prepend it to the number. We need to distinguish 3 cases:

• The first two digits are different, like 12..., then we get 212..., so the start is undulant, and won't affect whether the entire number is undulant.
• The first two digits are the same, like 11..., then we get 111.... Now the start is not undulant, but the number wasn't undulant anyway, so this won't affect the result either.
• If the number only has one digit, the digit at index 1 will be the first digit (because CJam's array indexing loops around the end), so this results in two identical digits, and the number is not undulant.

Now looking at the code in detail:

l_1=\+{_@-\}*;]_1=\+{_@*\}*;]{W>},!
_1=\+                              "Prepend second digit.";
{_@-\}*                       "This fold gets the differences of consecutive elments.";
;]                     "Drop the final element and collect in an aray.";
_1=\+                "Prepend second element.";
{_@*\}*         "This fold gets the products of consecutive elments.";
;]       "Drop the final element and collect in an aray.";
{W>},  "Filter out non-negative numbers.";
! "Logical not.";


I'm sure there is a shorter way to actually check digits (of length greater 1) for whether they are undulant (in particular, without using two folds), but I couldn't find it yet.

### Prolog 87 bytes

u(X) :- number_codes(X,C),f(C).
f([_,_]).
f([A,B,C|L]) :- (A<B,B>C;A>B,B<C),f([B,C|L]).


To run it, just save it as golf.pl, open a prolog interpreter (e.g. gprolog) in the same directory then do:

consult(golf).
u(101010).


It will give true if the number is undulant, otherwise just no.

# Mathematica, 46 bytes

#!=Sort@#&&#!=Reverse@Sort@#&[IntegerDigits@n]


Examples (spaces are not required):

# != Sort@# && # != Reverse@Sort@# &[IntegerDigits@5]
# != Sort@# && # != Reverse@Sort@# &[IntegerDigits@123]
# != Sort@# && # != Reverse@Sort@# &[IntegerDigits@132]
# != Sort@# && # != Reverse@Sort@# &[IntegerDigits@321]

(*  out *)
False  False  True  False


## Scala, 141 133 129 97 bytes

def u(n:Int):Boolean=n>9&&{
val a=n%10
val b=(n/10)%10
a!=b&&n<99||(a-b*b-(n/100)%10)<0&&u(n/10)}


With a = n % 10, b = (n/10) % 10, c = (n/100) % 10

if a > b and b < c or
a < b and b > c


Then a-b * b-c is either x*-y or -x*y with x and y as positive numbers, and the product is in both cases negative, but for -x*-y or x*y (a < b < c or a > b > c) the product is always positive.

The rest of the code is handling special cases: one digit, two digits, two identical digits.

# Julia 0.6, 62 bytes

f(x,a=sign.(diff(digits(x))))=x>9&&-a*a==(-1).^(1:endof(a))


Takes in a number, returns true for Undulant, and false for not. Eg f(163) returns true.

f(x,a=sign.(diff(digits(x))))=x>9&&-a*a==(-1).^(1:endof(a))
f(x,                        )                                   # function definition
a=sign.(diff(digits(x)))                                    # default 2nd argument is array of differences of signs of digits
x>9&&                             # short circuiting and to catch cases under 10
-a*a                      # make the first element of a always -1
==(-1).^(1:endof(a))  # check that a is an array of alternating -1 and 1 of correct length
`

Try it online!