24
\$\begingroup\$

Everyone knows log scales are for quitters. Therefore, you must write a program or function that de-quitifies a bar graph with a log scale given a base.

The bar graph input is taken as a single string which is a list of bars, where each bar of the log scale bar graph is separated by the printable (or whitespace) delimiter of your choosing (so 0x09-0x0A + 0x20-0x7E) and composed of a printable non-whitespace (so 0x21-0x7E) filler character of your choosing.

The program or function outputs a single string which is a list of bars, where each bar is separated by the same delimiter the input was separated by and is composed of the same filler character the input was composed of.

Example

We choose a delimiter of "\n" (one newline) and a filler character of "#". The input passed to our program or function is:

base=2 and string=

####
##
######
###

The code would find that the lengths of the bars are [4,2,6,3]. It would compute the anti-log of each length with base 2 to get [2^4,2^2,2^6,2^3] = [16,4,64,8]. Then, the lengths are output in linear scale bar format:

################
####
################################################################
########

Input / Output

Your program or function may input and output in any reasonable format.

The input base is guaranteed to be an integer greater than 1. You may assume the base is less than 256. The string input is guaranteed to fully match the regex (f+s)+f+, where f and s are replaced with your filler and delimiter respectively.

The string output must fully match the regex (f+s)+f+, where f and s are replaced with the same filler and delimiter respectively. The output may optionally have a trailing newline.

The output and input may also be a list of strings instead of delimited by a substring, though it must be possible to understand which bar is which.

Testcases

(assume filler is # and delimiter is \n)

base
-
input string
-
output string
-----
2
-
####
##
######
###
-
################
####
################################################################
########
-----
3
-
##
#
###
#
-
#########
###
###########################
###
-----
100
-
#   I am not the delimiter
###  nor the filler
-
Anything (You do not have to handle input which does not match the regex)
-----
1
-
###
#######
###################################################
- 
Anything (You do not have to handle bases less than or equal to 1).
-----
5
-
####
##
###
#
-
#################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################    
#########################
#############################################################################################################################
#####
-----
2
-
#
#
##
##
#
##
#
#
#
#
##
##
#
#
##
#
-
##
##
####
####
##
####
##
##
##
##
####
####
##
##
####
##
\$\endgroup\$

25 Answers 25

6
\$\begingroup\$

x86 32-bit machine code function, 21 bytes

x86-64 machine code function, 22 bytes

The 1B saving in 32-bit mode requires using separator = filler-1, e.g. fill=0 and sep=/. The 22-byte version can use an arbitrary choice of separator and filler.


This is the 21-byte version, with input-separator = \n (0xa), output-filler = 0, output-separator = / = filler-1. These constants can be easily changed.

; see the source for more comments
; RDI points to the output buffer,  RSI points to the src string
; EDX holds the base
; This is the 32-bit version.
; The 64-bit version is the same, but the DEC is one byte longer (or we can just mov al,output_separator)
08048080 <str_exp>:
 8048080:       6a 01           push   0x1
 8048082:       59              pop    ecx           ; ecx = 1 = base**0
 8048083:       ac                      lods   al,BYTE PTR ds:[esi]  ; skip the first char so we don't do too many multiplies

; read an input row and accumulate base**n as we go.
08048084 <str_exp.read_bar>:
 8048084:       0f af ca        imul   ecx,edx       ; accumulate the exponential
 8048087:       ac              lods   al,BYTE PTR ds:[esi]
 8048088:       3c 0a           cmp    al,0xa        ; input_separator = newline
 804808a:       77 f8           ja     8048084 <str_exp.read_bar>
 ; AL = separator or terminator
 ; flags = below (CF=1) or equal (ZF=1).  Equal also implies CF=0 in this case.

 ; store the output row
 804808c:       b0 30           mov    al,0x30       ; output_filler
 804808e:       f3 aa           rep stos BYTE PTR es:[edi],al  ; ecx bytes of filler
 8048090:       48              dec    eax           ; mov al,output_separator 
 8048091:       aa              stos   BYTE PTR es:[edi],al  ;append delim

 ; CF still set from the inner loop, even after DEC clobbers the other flags
 8048092:       73 ec           jnc    8048080 <str_exp>  ; new row if this is a separator, not terminator

 8048094:       c3              ret    

08048095  <end_of_function>
; 0x95 - 0x80 = 0x15 = 21 bytes

The 64-bit version is 1 byte longer, using a 2-byte DEC or a mov al, output_separator. Other than that, the machine-code is the same for both versions, but some register names change (e.g. rcx instead of ecx in the pop).

Sample output from running the test program (base 3):

$ ./string-exponential $'.\n..\n...\n....' $(seq 3);echo 
000/000000000/000000000000000000000000000/000000000000000000000000000000000000000000000000000000000000000000000000000000000/

Algorithm:

Loop over the input, doing exp *= base for every filler char. On delimiters and the terminating zero byte, append exp bytes of filler and then a separator to the output string and reset to exp=1. It's very convenient that the input is guaranteed not to end with both a newline and a terminator.

On input, any byte value above the separator (unsigned compare) is treated as filler, and any byte value below the separator is treated as an end-of-string marker. (Checking explicitly for a zero-byte would take an extra test al,al vs. branching on flags set by the inner loop).


The rules only allow a trailing separator when it's a trailing newline. My implementation always appends the separator. To get the 1B saving in 32-bit mode, that rule requires separator = 0xa ('\n' ASCII LF = linefeed), filler = 0xb ('\v' ASCII VT = vertical tab). That's not very human-friendly, but satisfies the letter of the law. (You can hexdump or
tr $'\v' x the output to verify that it works, or change the constant so the output separator and filler are printable. I also noticed that the rules seem to require that it can accept input with the same fill/sep it uses for output, but I don't see anything to gain from breaking that rule.).


NASM/YASM source. Build as 32 or 64-bit code, using the %if stuff included with the test program or just change rcx to ecx.

input_separator equ 0xa  ; `\n` in NASM syntax, but YASM doesn't do C-style escapes

output_filler equ '0'                 ; For strict rules-compliance, needs to be input_separator+1
output_separator equ output_filler-1  ; saves 1B in 32-bit vs. an arbitrary choice
    ;; Using output_filler+1 is also possible, but isn't compatible with using the same filler and separator for input and output.

global str_exp
str_exp:                        ; void str_exp(char *out /*rdi*/, const char *src /*rsi*/,
                                ;              unsigned base /*edx*/);
.new_row:
    push   1
    pop    rcx                  ; ecx=1 = base**0

    lodsb                       ; Skip the first char, since we multiply for the separator
.read_bar:
    imul   ecx, edx             ; accumulate the exponential
    lodsb
    cmp    al, input_separator
    ja .read_bar                ; anything > separator is treated as filler
    ; AL = separator or terminator
    ; flags = below (CF=1) or equal (ZF=1).  Equal also implies CF=0, since x-x doesn't produce carry.

    mov    al, output_filler
    rep stosb                   ; append ecx bytes of filler to the output string
%if output_separator == output_filler-1
    dec   eax         ; saves 1B in the 32-bit version.  Use dec even in 64-bit for easier testing
%else
    mov    al, output_separator
%endif
    stosb                       ; append the delimiter

    ; CF is still set from the .read_bar loop, even if DEC clobbered the other flags
    ; JNC/JNB here is equivalent to JE on the original flags, because we can only be here if the char was below-or-equal the separator
    jnc .new_row            ; separator means more rows, else it's a terminator
    ; (f+s)+f+ full-match guarantees that the input doesn't end with separator + terminator
    ret

The function follows the x86-64 SystemV ABI, with signature
void str_exp(char *out /*rdi*/, const char *src /*rsi*/, unsigned base /*edx*/);

It only informs the caller of the length of the output string by leaving a one-past-the-end pointer to it in rdi, so you could consider this the return value in a non-standard calling convention.

It would cost 1 or 2 bytes (xchg eax,edi) to return the end-pointer in eax or rax. (If using the x32 ABI, pointers are guaranteed to be only 32 bits, otherwise we have to use xchg rax,rdi in case the caller passes a pointer to a buffer outside the low 32 bits.) I didn't include this in the version I'm posting because there are workarounds the caller can use without getting the value from rdi, so you could call this from C with no wrapper.

We don't even null-terminate the output string or anything, so it's only newline-terminated. It would take 2 bytes to fix that: xchg eax,ecx / stosb (rcx is zero from rep stosb.)

The ways to find out the output-string length are:

  • rdi points to one-past-the-end of the string on return (so the caller can do len=end-start)
  • the caller can just know how many rows were in the input and count newlines
  • the caller can use a large zeroed buffer and strlen() afterwards.

They're not pretty or efficient (except for using the RDI return value from an asm caller), but if you want that then don't call golfed asm functions from C. :P


Size/range limitations

The max output string size is only limited by virtual memory address-space limitations. (Mainly that current x86-64 hardware only supports 48 significant bits in virtual addresses, split in half because they sign-extend instead of zero-extend. See the diagram in the linked answer.)

Each row can only have a maximum of 2**32 - 1 filler bytes, since I accumulate the exponential in a 32-bit register.

The function works correctly for bases from 0 to 2**32 - 1. (Correct for base 0 is 0^x = 0, i.e. just blank lines with no filler bytes. Correct for base 1 is 1^x = 1, so always 1 filler per line.)

It's also blazingly fast on Intel IvyBridge and later, especially for large rows being written to aligned memory. rep stosb is an optimal implementation of memset() for large counts with aligned pointers on CPUs with the ERMSB feature. e.g. 180**4 is 0.97GB, and takes 0.27 seconds on my i7-6700k Skylake (with ~256k soft page-faults) to write to /dev/null. (On Linux the device-driver for /dev/null doesn't copy the data anywhere, it just returns. So all of the time is in the rep stosb and the soft page-faults that triggers when touching the memory for the first time. It's unfortunately not using transparent hugepages for the array in the BSS. Probably an madvise() system call would speed it up.)

Test program:

Build a static binary and run as ./string-exponential $'#\n##\n###' $(seq 2) for base 2. To avoid implementing an atoi, it uses base = argc-2. (Command-line length limits prevent testing ridiculously large bases.)

This wrapper works for output strings up to 1 GB. (It only makes a single write() system call even for gigantic strings, but Linux supports this even for writing to pipes). For counting characters, either pipe into wc -c or use strace ./foo ... > /dev/null to see the arg to the write syscall.

This takes advantage of the RDI return-value to calculate the string length as an arg for write().

;;; Test program that calls it
;;; Assembles correctly for either x86-64 or i386, using the following %if stuff.
;;; This block of macro-stuff also lets us build the function itself as 32 or 64-bit with no source changes.

%ifidn __OUTPUT_FORMAT__, elf64
%define CPUMODE 64
%define STACKWIDTH 8    ; push / pop 8 bytes
%define PTRWIDTH 8
%elifidn __OUTPUT_FORMAT__, elfx32
%define CPUMODE 64
%define STACKWIDTH 8    ; push / pop 8 bytes
%define PTRWIDTH 4
%else
%define CPUMODE 32
%define STACKWIDTH 4    ; push / pop 4 bytes
%define PTRWIDTH 4
%define rcx ecx      ; Use the 32-bit names everywhere, even in addressing modes and push/pop, for 32-bit code
%define rsi esi
%define rdi edi
%define rsp esp
%endif


global _start
_start:
    mov  rsi, [rsp+PTRWIDTH + PTRWIDTH*1]  ; rsi = argv[1]
    mov  edx, [rsp]          ; base = argc
    sub  edx, 2              ; base = argc-2  (so it's possible to test base=0 and base=1, and so ./foo $'xxx\nxx\nx' $(seq 2) has the actual base in the arg to seq)
    mov  edi, outbuf         ; output buffer.  static data is in the low 2G of address space, so 32-bit mov is fine.  This part isn't golfed, though

    call str_exp             ; str_exp(outbuf, argv[1], argc-2)
    ;  leaves RDI pointing to one-past-the-end of the string
    mov  esi, outbuf

    mov  edx, edi
    sub  edx, esi               ; length = end - start

%if CPUMODE == 64 ; use the x86-64 ABI
    mov  edi, 1                 ; fd=1 (stdout)
    mov  eax, 1                 ; SYS_write  (Linux x86-64 ABI, from /usr/include/asm/unistd_64.h)
    syscall                     ; write(1, outbuf, length);

    xor edi,edi
    mov eax,231   ; exit_group(0)
    syscall


%else  ; Use the i386 32-bit ABI (with legacy int 0x80 instead of sysenter for convenience)
    mov ebx, 1
    mov eax, 4                  ; SYS_write (Linux i386 ABI, from /usr/include/asm/unistd_32.h)
    mov ecx, esi  ; outbuf
    ; 3rd arg goes in edx for both ABIs, conveniently enough
    int 0x80                    ; write(1, outbuf, length)

    xor ebx,ebx
    mov eax, 1
    int 0x80     ; 32-bit ABI _exit(0)
%endif


section .bss
align 2*1024*1024 ; hugepage alignment (32-bit uses 4M hugepages, but whatever)
outbuf:    resb 1024*1024*1024 * 1
; 2GB of code+data is the limit for the default 64-bit code model.
; But with -m32, a 2GB bss doesn't get mapped, so we segfault.  1GB is plenty anyway.

This was a fun challenge that lent itself very well to asm, especially x86 string ops. The rules are nicely designed to avoid having to handle a newline and then a terminator at the end of the input string.

An exponential with repeated multiplication is just like multiplying with repeated addition, and I needed to loop to count chars in each input row anyway.

I considered using one-operand mul or imul instead of the longer imul r,r, but its implicit use of EAX would conflict with LODSB.


I also tried SCASB instead of load and compare, but I needed xchg esi,edi before and after the inner loop, because SCASB and STOSB both use EDI. (So the 64-bit version has to use the x32 ABI to avoid truncating 64-bit pointers).

Avoiding STOSB is not an option; nothing else is anywhere near as short. And half the benefit of using SCASB is that AL=filler after leaving the inner loop, so we don't need any setup for REP STOSB.

SCASB compares in the other direction from what I had been doing, so I needed to reverse the comparisons.

My best attempt with xchg and scasb. Works, but isn't shorter. (32-bit code, using the inc/dec trick to change filler into separator).

; SCASB version, 24 bytes.  Also experimenting with a different loop structure for the inner loop, but all these ideas are break-even at best
; Using separator = filler+1 instead of filler-1 was necessary to distinguish separator from terminator from just CF.

input_filler equ '.'    ; bytes below this -> terminator.  Bytes above this -> separator
output_filler equ input_filler       ; implicit
output_separator equ input_filler+1  ; ('/') implicit

 8048080:       89 d1                   mov    ecx,edx    ; ecx=base**1
 8048082:       b0 2e                   mov    al,0x2e    ; input_filler= .
 8048084:       87 fe                   xchg   esi,edi
 8048086:       ae                      scas   al,BYTE PTR es:[edi]

08048087 <str_exp.read_bar>:
 8048087:       ae                      scas   al,BYTE PTR es:[edi]
 8048088:       75 05                   jne    804808f <str_exp.bar_end>
 804808a:       0f af ca                imul   ecx,edx           ; exit the loop before multiplying for non-filler
 804808d:       eb f8                   jmp    8048087 <str_exp.read_bar>   ; The other loop structure (ending with the conditional) would work with SCASB, too.  Just showing this for variety.
0804808f <str_exp.bar_end>:

; flags = below if CF=1 (filler<separator),  above if CF=0 (filler<terminator)
; (CF=0 is the AE condition, but we can't be here on equal)
; So CF is enough info to distinguish separator from terminator if we clobber ZF with INC

; AL = input_filler = output_filler
 804808f:       87 fe                   xchg   esi,edi
 8048091:       f3 aa                   rep stos BYTE PTR es:[edi],al
 8048093:       40                      inc    eax         ; output_separator
 8048094:       aa                      stos   BYTE PTR es:[edi],al
 8048095:       72 e9                   jc     8048080 <str_exp>   ; CF is still set from the inner loop
 8048097:       c3                      ret    

For an input of ../.../., produces ..../......../../. I'm not going to bother showing a hexdump of the version with separator=newline.

\$\endgroup\$
4
\$\begingroup\$

Mathematica 41 38 Bytes

-3 Bytes thanks to LLlAMnYP

This takes input as a list of strings followed by an integer. Output is also a list of strings.

""<>"#"~Table~#&/@(#2^StringLength@#)&

Explanation:

                   StringLength@# & - find length of each string in first input
                   #2^               & - raise to power of second input
                /@(                 )  - Uses each of these numbers on an inner function of ...
    "#"~Table~#&                       - Create arrys of specific length using character "#"
 ""<>                                  - Join arrays of characters together to make strings

Old version, 41 Bytes

"#"~StringRepeat~#&/@(#2^StringLength@#)&
\$\endgroup\$
  • \$\begingroup\$ "" <> "#"~Table~# is 3 bytes shorter than "#"~StringRepeat~#, probably golfable further as well. \$\endgroup\$ – LLlAMnYP May 31 '17 at 10:45
3
\$\begingroup\$

Japt, 7 bytes

Takes the graph as an array of strings with " as the filler, and the base as an integer.

£QpVpXl

Try it online

Add }R to the end in order to take the graph as a newline separated string instead. (Try it)


Explanation

    :Implicit input of array U.
£   :Map over the array, replacing each element with ...
Q   :the " character ...
p   :repeated ...
V   :integer input ...
p   :to the power of ...
Xl  :the length of the current element times.
    :Implicit output of result.
\$\endgroup\$
3
\$\begingroup\$

MATL, 14 11 bytes

Y'iw^1HL(Y"

Delimiter is space. Filler is any character other than space.

Try it online!

Explanation

       % Implicit input: string
       %   STACK: '## # ### #'
Y'     % Run-length encoding
       %   STACK: '# # # #', [2 1 1 1 3 1 1]
i      % Input: number
       %   STACK: '# # # #', [2 1 1 1 3 1 1], 3
w      % Swap
       %   STACK: '# # # #', 3, [2 1 1 1 3 1 1]
^      % Power, element-wise
       %   STACK: '# # # #', [9 3 3 3 9 3 3]
1      % Push 1
       %   STACK: '# # # #', [9 3 3 3 27 3 3], 1
HL     % Push [2 2 1j]. When used as an index, this means 2:2:end
       %   STACK: '# # # #', [9 3 3 3 27 3 3], 1, [2 2 1j]
(      % Write specified value at specified entries
       %   STACK: '# # # #', [9 1 3 1 27 1 3]
Y"     % Run-length decoding
       %  STACK: '######### ### ########################### ###'
       % Implicit display
\$\endgroup\$
  • \$\begingroup\$ This doesn't seem to work; the length of each line in the output for the test case you included in your TIO should be 9,3,27,9 but instead it's 6,3,9,3. \$\endgroup\$ – Shaggy May 31 '17 at 13:33
  • \$\begingroup\$ @Shaggy You are totally right. Thanks for noticing. I made a mistake in my latest edit. I have rolled back to the previous version, ehich is correct \$\endgroup\$ – Luis Mendo May 31 '17 at 13:44
  • \$\begingroup\$ Couldn't figure out how it was working from the explanation - then I clicked through to the TIO! :D \$\endgroup\$ – Shaggy May 31 '17 at 13:48
  • 1
    \$\begingroup\$ @Shaggy I just added an explanation for this version, hopefully clearer! \$\endgroup\$ – Luis Mendo May 31 '17 at 14:04
3
\$\begingroup\$

Haskell, 37 33 bytes

4 bytes shaved off thanks to sudee

\b->map(\x->'#'<$[1..b^length x])

Description:

\b->                               -- take an integer b as the first input input
    map(\x->                    )  -- apply the following to every element x in the second input
            '#'<$[1..b^length x]   ---- replicate '#' (b^(length x)) times

Disappointingly, this is 2 bytes much shorter than the way-harder-to-read pointfree version:

map.(flip replicate '#'.).(.length).(^)
\$\endgroup\$
  • \$\begingroup\$ The input should be a single string \$\endgroup\$ – bartavelle May 31 '17 at 12:39
  • \$\begingroup\$ @bartavelle, not necessarily. \$\endgroup\$ – Shaggy May 31 '17 at 13:54
  • \$\begingroup\$ That's what I understand by The bar graph input is taken as a single string ... \$\endgroup\$ – bartavelle May 31 '17 at 13:56
  • 1
    \$\begingroup\$ @bartavelle: The output and input may also be a list of strings instead of delimited by a substring, though it must be possible to understand which bar is which. \$\endgroup\$ – Julian Wolf May 31 '17 at 14:44
  • 2
    \$\begingroup\$ You can replace replicate(b^length x)'#' with '#'<$[1..b^length x]. \$\endgroup\$ – sudee May 31 '17 at 14:56
3
\$\begingroup\$

ReRegex, 105 bytes

#import math
(\d+)\n((;.*\n)*)(_+)/$1\n$2;$1^d<$4>/^\d+\n((;\d+\n?)+)$/$1/^((_*\n)*);(\d+)/$1u<$3>/#input

Try it online!

ReRegex is like Retina's ugly cousin that gives all the effort to Regular Expressions, instead of having it's own fancy operators.

Of course, it also has #import and #input to save both hardcoding input, and re-writing the same expressions over and over again.

Explained.

Takes input in the form of:

2
____
__
______
___

on STDIN, and gives output like

________________
____
________________________________________________________________
________

Firstly, the program imports the Math Library, which of course is entirely written in ReRegex. The bulk of this is then three regular expressions.

(\d+)\n((;.*\n)*)(_+)   ->  $1\n$2;$1^d<$4>
^\d+\n((;\d+\n?)+)$     ->  $1
^((_*\n)*);(\d+)        ->  $1u<$3>

The first matches our input base, and seeks a line of unary after it. it then, replaces that line with ;$1^d<$4>, which is the base, to the power of the (In Decimal) Unary. The Math library handles the base conversion and the exponent. A ; is placed at the start to identify it later as finished.

The second, matches the base, then many lines of ;, before ending. If this matches the entire thing, it snips off the base. leaving uf with just the answers and ;s.

The last, matches just unary at the start, optionally, then a ; answer. It then transforms that answer into unary again, without the ;.

Because the output isn't matched by the first regex, it doesn't loop infinitely, and so our solution is outputted.

\$\endgroup\$
2
\$\begingroup\$

Python 2, 52 36 bytes

Input and output are taken as arrays of strings. # is the filler.

lambda s,n:['#'*n**len(l)for l in s]

Try it online!

\$\endgroup\$
2
\$\begingroup\$

Röda, 19 bytes

f n,s{s|["#"*n^#_]}

Try it online!

Takes an array as input and returns a stream of values as output.

Explanation

f n,s{s|["#"*n^#_]}              n is the number and s is the array of strings consisting of #s
      s|                         Push the each value of s to the stream
        [        ]               For each push
         "#"*                     "#" repeated
             n^#_                 n raised to the length of the string
\$\endgroup\$
2
\$\begingroup\$

Haskell, 32 bytes

f b=map$foldr(\_->([1..b]>>))"#"

Try it online! Example usage: f 3 ["##","#","###","#"] returns ["#########","###","###########################","###"].

Use mapM putStrLn $ f 3 ["##","#","###","#"] to get a more visually pleasing output:

#########
###
###########################
###
\$\endgroup\$
  • \$\begingroup\$ Just commenting here because I can't comment on the post you deleted... try sum[sum[]^sum[],sum[]^sum[]]. \$\endgroup\$ – Ørjan Johansen Jun 5 '17 at 17:42
2
\$\begingroup\$

05AB1E, 9 bytes

Bars are seperated by spaces, output character is same as input character.

¬Š#€gm×ðý

Try it online!

¬Š#€gm×ðý   Arguments: n, s
¬           Head, get bar character
 Š          Rearrange stack to get: s, n, bar-character
  #         Split s on spaces
   €g       Map to length
     m      n to that power
      ×     That many bar-characters
       ðý   Join on space
            Implicit output
\$\endgroup\$
1
\$\begingroup\$

PHP, 69 Bytes

<?foreach($_GET[1]as$l)echo str_pad("",$_GET[0]**strlen($l),"#")."
";

Try it online!

\$\endgroup\$
  • \$\begingroup\$ This returns with a leading newline, which is not allowed by the regex. You can use [str_pad]."\n" instead of "\n".[str_pad] to fix this (+1 byte). Also, you may assume what the filler is, so you could save two bytes in $l[0] by changing it to "#". \$\endgroup\$ – fireflame241 May 30 '17 at 23:41
  • \$\begingroup\$ @fireflame241 Done Thank You \$\endgroup\$ – Jörg Hülsermann May 30 '17 at 23:56
1
\$\begingroup\$

Jelly, 7 bytes

ṁL*@¥¥€

A monadic link taking and returning lists of the bars (themselves lists of characters, AKA strings) the filler character is flexible.

Try it online! (the footer prettifies the resulting list by joining its elements with newlines.)

How?

ṁL*@¥¥€ - Main link: list of list of characters, bars; number, base
     ¥€ - last two links as a dyad for €ach bar in bars:
    ¥   -   last two links as a dyad:
 L      -     length (of a bar)
  *@    -     exponentiate (swap @rguments) (base ^ length)
ṁ       -   mould like (e.g. moulding "##" like 8 yields "########")

Alternative 7-byter: ṁ"L€*@¥ - get length of each bar (L€), raise base to that power (*@), then zip (") the list and that applying the mould dyad () between the two.

\$\endgroup\$
  • \$\begingroup\$ 4 quicks and 3 actual links? This challenge is quite heavy on controlling the flow of data... \$\endgroup\$ – ETHproductions May 31 '17 at 1:08
  • \$\begingroup\$ Yeah, there might be a shorter solution available... \$\endgroup\$ – Jonathan Allan May 31 '17 at 1:09
  • \$\begingroup\$ @JonathanAllan I'm afraid there isn't. \$\endgroup\$ – Erik the Outgolfer May 31 '17 at 9:39
  • \$\begingroup\$ @ETHproductions It's actually one link as a whole. The explanation could've pretty much been just one line. \$\endgroup\$ – Erik the Outgolfer May 31 '17 at 9:40
1
\$\begingroup\$

Ruby, 29 bytes

->x,y{x.map{|z|?#*y**z.size}}

Try it online!

Yeah, I found out last week that ?# produces a single-character string. I've no idea why this feature exists but I'm sure glad it does.

\$\endgroup\$
  • 1
    \$\begingroup\$ The ?X operator, where X is some character, is the "get the default representation of this character" operator. In Ruby < 1.9, it would return the Unicode code point of the character, because that's how characters were defined, but now it returns a string containing the character. It's part of a general shift towards more consistent Unicode handling in Ruby. \$\endgroup\$ – Tutleman May 31 '17 at 10:57
  • \$\begingroup\$ @Turtleman, is there any hysterical raisin for why ?X is used? A lot of Ruby's quirkier conventions, like the plethora of $-variables, exist because of familiarity with Perl. \$\endgroup\$ – ymbirtt May 31 '17 at 13:49
1
\$\begingroup\$

JavaScript (ES8), 39 bytes

Takes the base as an integer and the graph as an array of strings with any character as the filler, using currying syntax.

b=>a=>a.map(x=>x.padEnd(b**x.length,x))

Try it

f=
b=>a=>a.map(x=>x.padEnd(b**x.length,x))
oninput=_=>o.innerText=f(i.value)(j.value.split`\n`).join`\n`
o.innerText=f(i.value=2)((j.value=`####\n##\n######\n###`).split`\n`).join`\n`
*{box-sizing:border-box}#i,#j{margin:0 0 5px;width:200px}#j{display:block;height:100px
<input id=i type=number><textarea id=j></textarea><pre id=o>


Alternative, 49 bytes

This version takes the graph as a newline separated string, again with any character as filler.

b=>s=>s.replace(/.+/g,m=>m.padEnd(b**m.length,m))
\$\endgroup\$
  • \$\begingroup\$ Don't think you need the m flag on the regex, by default . doesn't match newlines. \$\endgroup\$ – ETHproductions May 31 '17 at 0:02
  • \$\begingroup\$ Hmm, don't know where that came from - the perils of trying to golf from a phone. Thanks for pointing it out, @ETHproductions. \$\endgroup\$ – Shaggy May 31 '17 at 7:19
0
\$\begingroup\$

Mathematica, 86 bytes

(s=#2^StringLength[StringSplit@#1];StringJoin/@Table[Table["#",s[[i]]],{i,Length@s}])&

input

["####\n##\n######\n###", 2]

\$\endgroup\$
  • \$\begingroup\$ ok...Fixed...... \$\endgroup\$ – J42161217 May 31 '17 at 4:00
0
\$\begingroup\$

Octave, 42 bytes

@(b,s)[(1:max(k=b.^sum(s'>32)')<=k)+32 '']

*The string input/output doesn't fully match the regex but it is possible to understand which bar is which.

A function takes as input base b and a 2D array of chars s containing "!" and the output is also an array of chars.

Try it online!

Explanation:

                       s'>32               % logical array of input represents 1 for filler and 0 for spaces
                   sum(     )'             % an array containing length of each string 
              k=b.^                        % exponentiate ( lengths of output)
        1:max(                )            % range form 1 to max of output lengths
                               <=k         % logical array of output represents 1 for filler and 0 for spaces
      [(                          )+32 ''] % convert the logical array to char array.
\$\endgroup\$
0
\$\begingroup\$

CJam, 20 bytes

q~:A;N/{,A\#"#"e*N}%

Input format

Input is required in the following format:

"##
####
######"2
\$\endgroup\$
0
\$\begingroup\$

Charcoal, 11 bytes

NβWS«PXβLι↓

Try it online! Link is to verbose version of code. I/O is as a list of strings of - characters (note that you need an empty line to terminate the list).

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

V, 27 bytes

The basic idea is that we add a ' to each row (n^0), and then for each # we replace the 's in the line with [input] * '. At the end I swapped all of the ' for # again

Àé'ld0ÎA'
ò/#
"_xÓ'/"òÍ'/#

Try it online!

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

R, 35 bytes

function(s,b)strrep('#',b^nchar(s))

an anonymous function which takes the strings as a list and the base, and returns a list of strings.

Try it online!

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

05AB1E, 10 bytes

U|v1Xygm×,

The filer character is 1 and the delimiter is a newline.

Try it online!

U          # Store the base in X
 |         # Get the rest of input as a list of lines
  v        # For each...
   1       #   Push 1
    X      #   Push the base
     y     #   Push this bar
      g    #   Get the length
       m   #   Push a**b
        ×, #   Print a string of #s with that length
\$\endgroup\$
0
\$\begingroup\$

Retina, 62 bytes

ms`^(?=.*¶(.*))
#;$1$*#;
{`#(?=#*;(#+);#)
$1
}m`#$

;#+;|¶.*$

Try it online! After all, a bar graph is just a list of unary numbers. Takes input as the graph (using #s) followed by the base in decimal (to avoid confusion). Explanation: The first replacement prefixes 1 and the base to each line of the graph. The second replacement then multiplies the first number on each line by the second, as long as the third number is nonzero. The third replacement then decrements the third number on each line. These two replacements are repeated until the third number becomes zero. The last replacement deletes the base everywhere, leaving the desired result.

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

Convex, 9 bytes

f{,#'#*N}

Try it online!

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

Alice, 23 bytes

/'/dI
\I!wO&K/h.n$@?~E&

Try it online!

Not only am I not a quitter, but I'm so committed to making the point properly that I use ! as the filler. That will certainly get the reader's attention.

Explanation

Mirrors are retained in this explanation to make it more clear when the program switches between cardinal and ordinal modes.

/I/!/wI&/h.n$@?~E&\'!dOK

/I                        % input base
  /!/                     % store onto tape as integer
     w                    % push return address
      I                   % input next line
       &/h                % get length (by adding 1 for each character in the string)
          .n$@            % terminate if zero
              ?~E         % get base from tape and raise to power
                 &\'!     % push "!" onto the stack that many times
                     d    % combine into a single string
                      O   % output string with newline
                       K  % return to stored address (without popping it from the return address stack)
\$\endgroup\$
0
\$\begingroup\$

Perl 6, 26 bytes

{map '#'x$^b** *.comb,@^a}

The list of input strings is in the first parameter, @^a. The second parameter $^b is the base. A list of output strings is returned.

\$\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.