Toggle Shift Key

25

2

Objective

Given an ASCII character, toggle its "shift-ness" on the standard US keyboard (ANSI-INCITS 154-1988 (R1999)), then output it.

Mapping

) ↔️ 0
! ↔️ 1
@ ↔️ 2
# ↔️ 3
$ ↔️ 4
% ↔️ 5
^ ↔️ 6
& ↔️ 7
* ↔️ 8
( ↔️ 9
" ↔️ '
+ ↔️ =
, ↔️ <
- ↔️ _
. ↔️ >
/ ↔️ ?
: ↔️ ;
(Capital Latin letters) ↔️ (Small Latin letters)
[ ↔️ {
\ ↔️ |
] ↔️ }
` ↔️ ~
(ASCII whitespaces and control characters are left intact)

Rule

  • Non-ASCII characters fall into don't care situation.

Examples

Given a character 0, output ).
Given a character C, output c.
Given a character (space), output .
Given a character \t (horizontal tab), output \t.
Given a character \a (bell), output \a.

Dannyu NDos

Posted 2020-02-22T22:06:25.107

Reputation: 2 087

Is it ok if we do the UK keyboard? Just four differences that I can tell. – ouflak – 2020-02-26T18:08:33.957

@ouflak I'm afraid not. – Dannyu NDos – 2020-02-26T20:01:15.427

Answers

8

JavaScript (Node.js),  110 ... 97  96 bytes

c=>(s=")0@2^6&7*8(9\"'+=-_:;`~")[s.indexOf(c)^1]||(B=Buffer)([([n]=B(c),n%127>32)<<4+n/64^n])+''

Try it online!

How?

We use a lookup table for these pairs:

) ↔️ 0
@ ↔️ 2
^ ↔️ 6
& ↔️ 7
* ↔️ 8
( ↔️ 9
" ↔️ '
+ ↔️ =
- ↔️ _
: ↔️ ;
` ↔️ ~

For all other characters, we use the following code:

(B = Buffer)([   // generate a buffer from a singleton array:
  (              //
    [n] = B(c),  //   n = ASCII code of the input character
    n % 127 > 32 //   1 if 32 < n < 127, or 0 otherwise
  )              //
  << 4 + n / 64  //   left-shifted by 4 if n < 64, or by 5 otherwise
  ^ n            //   XOR'ed with n
]) + ''          // end of Buffer(); coerce it to a string

which supports these 3 cases:

  • If \$32<n<64\$, we XOR the ASCII code with \$16\$, which is what we need for:

    ! ↔️ 1
    # ↔️ 3
    $ ↔️ 4
    % ↔️ 5
    , ↔️ <
    . ↔️ >
    / ↔️ ?
    
  • If \$64\le n<127\$, we XOR the ASCII code with \$32\$, which toggles the case of letters and also works for these pairs:

    [ ↔️ {
    \ ↔️ |
    ] ↔️ }
    
  • If \$n\le32\$ or \$n=127\$, the character is left unchanged (ASCII code XOR'ed with \$0\$).

Arnauld

Posted 2020-02-22T22:06:25.107

Reputation: 111 334

7

Retina 0.8.2, 34 bytes

T`-~A-]d';-?/.+,:"(*&^%$#@!)}-_`Ro

Try it online! Link includes test cases.

Explanation: The T command is Retina's transliteration operation. Among its special characters are:

  • `: Delimit the parts of the command
  • -: Introduce a range (this suppresses the following special characters so d-o just means defghijklmno)
  • _: Delete the matching character
  • d: 0-9
  • R: Reverse the next range
  • o: Take the source string as the range

The trick is therefore to create as many ranges as possible to reduce the length, plus avoiding having the - and _ characters where they would be interpreted as a special character and avoiding the ` at all by including it inside a range. Additionally arranging for the range to transliterate to its reversal allows that to be expressed very cheaply.

Neil

Posted 2020-02-22T22:06:25.107

Reputation: 95 035

6

x86-16, Genuine IBM PC, 32 31 29 bytes

00000000: b4f0 8ec0 bfe5 e8b3 3ab1 74a0 8200 f2ae  ........:.t.....
00000010: 3acb 7f02 f7db 268a 41ff cd29 c3         :.....&.A..).

Build with xxd -r.

Unassembled listing:

B4 F0       MOV  AH, 0F0H               ; BIOS segment address (F000H)
8E C0       MOV  ES, AX                 ; set ES to BIOS segment for SCASB
BF E8E5     MOV  DI, E8E5H              ; set to LABEL K10 
B3 3A       MOV  BL, 58                 ; size of table 
B1 74       MOV  CL, 58*2               ; size of both tables 
A0 0082     MOV  AL, BYTE PTR DS:[82H]  ; get input char from command line 
F2/ AE      REPNZ SCASB                 ; search BIOS table for char 
3A CB       CMP  CL, BL                 ; found in the lowercase table? 
7F 02       JG   UPPER                  ; if so, convert with uppercase table by adding
F7 DB       NEG  BX                     ; otherwise subtract the offset 
        UPPER: 
26:8A 41 FF MOV  AL, ES:[BX+DI-1]       ; get char from table
CD 29       INT  29H                    ; DOS fast write to console 
C3          RET                         ; return to DOS

Input/Output:

enter image description here

How does it work?

Well, the PC BIOS already contains all of the code and tables necessary for this since it actually handles the conversion of scan codes received from the keyboard to ASCII chars (in real/DOS mode at least). There's no way (that I know of) to actually hook this on just any "PC-compatible" BIOS, however if you know the location of the table in ROM (on the IBM PC it starts at F000:E8E5), you can use that.

On Page A-25 of Appendix A the IBM PC Technical Reference (listing of the entire source code of the PC BIOS) is the disassembly of this table:

enter image description here

Unfortunately, this address will be different on any given PC-clone BIOS and of course, there's no guarantee that it would even be implemented in the same way by anyone else. Thus, this submission is guaranteed to run only on a Genuine IBM PC. Go Big Blue!

640KB

Posted 2020-02-22T22:06:25.107

Reputation: 7 149

4

Bash + core utilities, 58 \$\cdots\$ 51 49 bytes

Added 47 50 bytes to fix a bug kindly pointed out by Giuseppe.
Saved 8 bytes thanks to Value Ink!!!
Saved 2 bytes thanks to Mitchell Spector!!!

a='"A-]`=<_>?)!@#$%^&*(;'
b=\'a-~+-:
tr $a$b $b$a

Try it online!

Noodle9

Posted 2020-02-22T22:06:25.107

Reputation: 2 776

1I like the idea behind this, but it doesn't seem to be coming out right. For example, 'A' => 'b'. Also, it's not actually bash -- it's "bash + core utilities", so I think the language listed should be changed accordingly. (tr isn't a bash built-in.) – Mitchell Spector – 2020-02-23T22:37:20.613

1@MitchellSpector Sorry it's a bit of a hack, did this from my phone! T_T Will fix it when I get a chance. – Noodle9 – 2020-02-23T23:01:28.647

1From your phone? That's impressive in itself! I don't think I could do that. – Mitchell Spector – 2020-02-23T23:03:12.573

@MitchellSpector Have termux which is very sweet! :-) – Noodle9 – 2020-02-23T23:17:04.190

I haven't seen that app, but it must be nice :) . @Noodle9 – Mitchell Spector – 2020-02-24T04:58:36.923

Do you really need the square brackets around each quote? The Wikipedia article on tr syntax doesn't seem to indicate the need for either unless you're using a special sequence like [:alnum:]. I think removing them won't affect the program, but I can't be sure until you fix the other issues that were brought up. Also, I feel like you don't need to escape the ! – Value Ink – 2020-02-25T05:35:07.983

@ValueInk I made the whole string a regex for the [0-9], [A-Z] and [a-z] regex's. – Noodle9 – 2020-02-25T17:41:17.063

Yes, but the tr page on Wikipedia says you don't need it. Might depend on the version of coreutils you have but the one on TIO works this way. Example with reduced charset

– Value Ink – 2020-02-25T18:17:39.617

@ValueInk Sweet! Just got back from holiday after a long journey. Will sort this out properly on my much missed box tomorrow - thanks! :-) – Noodle9 – 2020-02-25T19:59:31.237

@ValueInk Fixed! Nice one - using your no [] tip and it works, thanks! :-) – Noodle9 – 2020-02-26T17:14:38.970

1@MitchellSpector All fixed now. – Noodle9 – 2020-02-27T02:07:24.063

@Giuseppe All fixed now. – Noodle9 – 2020-02-27T02:07:59.257

1Nice solution! - – Mitchell Spector – 2020-02-27T02:25:54.183

I think you can shave off 2 bytes by removing the single quotes around '0-9'. – Mitchell Spector – 2020-02-27T02:32:25.670

@MitchellSpector Yes indeed, nice one - thanks! :-) – Noodle9 – 2020-02-27T02:51:21.770

By the way, I've just added an answer which is basically a port of this nice solution to pure bash (without using tr or other utilities). It's shorter than my earlier pure bash solution, which uses a different approach. (I tried to tag you in the answer, but apparently you can't do that.) – Mitchell Spector – 2020-02-27T06:12:20.517

Apparently you can’t tag people in answers, only in comments, and even then it seems you can only tag people who commented earlier. (This is unless I’m missing something, of course!) – Mitchell Spector – 2020-02-27T08:45:00.293

@MitchellSpector I see what you mean now, you can mention them but they don't get notified. Looks like a wrinkle in the software! If only the had access to people who ciuld fix this... T_T – Noodle9 – 2020-02-27T08:49:54.593

Exactly. But I think it’s on purpose, to prevent spamming people who haven’t expressed an interest in the answer. – Mitchell Spector – 2020-02-27T08:51:06.260

@MitchellSpector Always the bad ones who spoil things, makes sense. – Noodle9 – 2020-02-27T08:52:47.407

3

Python 3, 99 95 bytes

Same strategy as @Arnauld's answer.

-4 bytes thanks to @mypetlion

lambda c,s=")0!1@2#3$4%5^6&7*8(9\"'+=,<-_.>/?:;[{\\|]}`~":[c.swapcase(),s[s.find(c)^1]][c in s]

Try it online!

Explanation

  • s=")0!1@2#3$4%5^6&7*8(9\"'+=,<-_.>/?:;[{\\|]}`~" contains all pairs of special toggles.
  • s[s.index(c)^1] gets the index of the other character from the same pair
  • If the character is a Latin or control character (c in s is False), c.swapcase() is returned. c.swapcase() toggles the case of a Latin character, but doesn't affect control character.

Surculose Sputum

Posted 2020-02-22T22:06:25.107

Reputation: 317

1Change everything after the colon to [c.swapcase(),s[s.find(c)^1]][c in s] to save 4 bytes. – mypetlion – 2020-02-24T18:13:40.433

1Clever use of boolean index! – Surculose Sputum – 2020-02-24T18:26:54.447

1Thanks @mypetlion for spotting the erroneous extra space at the end! – Surculose Sputum – 2020-02-24T20:12:41.917

2

APL (Dyalog Extended), 86 bytesSBCS

This solution has been out-golfed but is preserved due to being interesting for its sourcing of the data.

Anonymous prefix lambda. Requires 0-based indexing (⎕IO←0).

{⍵∊a←↑'[!-~]{2}'⎕S'&'↓⍉↑'\w\w...'⎕R(5⍴'')↓11 71↑30↓⎕FMT⌂notes.keyboards:a⌷⍨~@1⊃⍸⍵=a⋄⍵}

Try it online!

This works by extracting the character pairs from a built-in note about keyboards.

{} "dfn"; argument character is :

⌂notes.keyboards the built-in string

⎕FMT ForMaT as a character matrix

30↓ drop the first 30 lines

11 71↑ take the first 11 lines' first 71 columns

 split into list of strings

'\w\w...'⎕R() PCRE Replace word-character,word-character,any-symbol,any-symbol with:

  '' the empty string

  5⍴ reshaped to length 5, padding with spaces; i.e. 5 spaces

 mix list of strings into character matrix

 transpose

 split matrix into list of strings

'[!-~]{2}'⎕R'&' PCRE Search and return any pair of printable ASCII characters

 mix list of character pairs into two-column matrix

a← store in a (for all)

⍵∊: if the argument character is a member of that matrix:

  ⍵= Boolean matrix mask where they are equal

   get indices where true

   pick the first (row,column) pair

  ~@1 negate the number at position 1 (the column; i.e. 1→0 and 0→1)

  a⌷⍨ use that to index into the matrix of all pairs

 else:

   the argument character

Adám

Posted 2020-02-22T22:06:25.107

Reputation: 37 779

Is taking the first 71 columns necessary? – Jonathan Allan – 2020-02-22T23:53:20.957

Could '\w\w...'⎕R(5⍴'') be replaced by a reverse-each line (so that the Caps<>Lock mapping is never used)? – Jonathan Allan – 2020-02-22T23:55:52.470

@JonathanAllan The 71 chops "Backspace" into "Backs" and reversing would expose "Enter" instead. – Adám – 2020-02-23T00:07:26.003

Ah, I see, so you'd need to ignore all characters paired with spaces too. – Jonathan Allan – 2020-02-23T00:11:16.600

2

Jelly,  38  37 bytes

“Æz⁶ɦG!€u<ʂCP]ƭƝ’œ?ØṖḟØB¤ḊØD;ṙ21,ƊyŒs

A monadic Link accepting a list of characters which yields a list of characters.

Try it online!

How?

Forms a mapping for non alphabetic characters, applies the translate atom, y, and then swaps case.

“Æz⁶ɦG!€u<ʂCP]ƭƝ’œ?ØṖḟØB¤ḊØD;ṙ21,ƊyŒs - Link: list of characters, S
“Æz⁶ɦG!€u<ʂCP]ƭƝ’                     - base 250 number              =53994992086540427749431907521542401
                        ¤             - nilad followed by link(s) as a nilad:
                   ØṖ                 -   printable characters       = !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
                      ØB              -   base-62 characters         =0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
                     ḟ                -   filter discard             = !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
                 œ?                   - lexicographical permutation  = '=<_>?;{|}~)!@#$%^&*("+,-./:[\]`
                         Ḋ            - dequeue                      ='=<_>?;{|}~)!@#$%^&*("+,-./:[\]`
                          ØD          - digit characters             =0123456789
                            ;         - concatenate                  =0123456789'=<_>?;{|}~)!@#$%^&*("+,-./:[\]`
                                 Ɗ    - last three links as a monad:
                              21      -   twenty-one
                             ṙ        -   rotate left                =)!@#$%^&*("+,-./:[\]`0123456789'=<_>?;{|}~
                                ,     -   pair                       =)!@#$%^&*("+,-./:[\]`0123456789'=<_>?;{|}~ and 0123456789'=<_>?;{|}~)!@#$%^&*("+,-./:[\]`
                                  y   - translate (S)
                                   Œs - swap case

Jonathan Allan

Posted 2020-02-22T22:06:25.107

Reputation: 67 804

2

Perl 5 -p, 92 91 bytes

-1 byte thanks to @ValueInk

s/[]a-z{}|\\[]/$&^$"/gei;y/0-9)!@#$%^&*(`~\-=_+';":,.\/<>?/)!@#$%^&*(0-9~`_+\-=":';<>?,.\//

Try it online!

Xcali

Posted 2020-02-22T22:06:25.107

Reputation: 7 671

/[]a-z{}|\\[]/ lets you cut out the extra backslash while still matching ] in your first regex. Also, why did you put those particular characters into its own s/// operation instead of wrapping them into the y///? I don't know much about Perl but I did use the Ruby y/// analogue (tr) in my own solution without needing to do a regex sub at all. Maybe that could help you out with golfing your solution. – Value Ink – 2020-02-25T05:03:00.540

You don't need the substitution, but it works out to be a couple of bytes shorter than accounting for those characters in the translation. – Xcali – 2020-02-25T17:59:48.727

Hmmm. Could you have 2 strings (one with upper only and one with lower only) and pass them into tr in different orders similar to what I did in my Ruby solution? I'm not sure how costly it is to declare variables in Perl though – Value Ink – 2020-02-26T00:27:47.690

No. tr/// is a quote-like operator in Perl. It does not do variable interpolation. You'd have to put it inside an eval, costing even more bytes. – Xcali – 2020-02-26T01:32:54.237

I figured it out; eval might cost bytes, but not as much as it saves. 77 bytes, probably can be golfed further depending on how the strings are set up

– Value Ink – 2020-02-26T02:19:28.470

2

05AB1E, 53 36 35 bytes

.šžQžiмDœ•1ĀêŽƵÆ{çIΣeÿí‚Ð"pkālrÙ•è‡

-1 byte implicitly thanks to @Grimmy due to his comment.

Try it online or verify all printable ASCII characters. NOTE: The TIO versions use the S•...•.I (builtin for the n'th permutation of a list) instead of œ•...•è (push all permutations of the string (extremely slow bottleneck for a string this large), and index into it).

Explanation:

.š        # Switch-case of the (implicit) input-character (if it's a letter)
  žQ      # Push all printable ASCII characters:
          #   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
    žiм   # Remove all letters [A-Za-z]:
          #   !"#$%&'()*+,-./0123456789:;<=>?@[\]^_`{|}~
  D       # Duplicate it
   œ      # Push a list of all permutations of this string
    •1ĀêŽƵÆ{çIΣeÿí‚Ð"pkālrÙ•
         "# Push compressed integer 540470878544692028277787799913384202055137480696656
     è    # Index it into the string-list of permutations:
          #   1'3457"908=<_>?)!@#$%^&*(;:,+./2{|}6-~[\]`
  ‡       # Transliterate the characters at the same indices
          # (after which the result is output implicitly)

See this 05AB1E tip of mine (section How to compress large integers?) to understand why •1ĀêŽƵÆ{çIΣeÿí‚Ð"pkālrÙ• is 540470878544692028277787799913384202055137480696656.

This number(+1) is generated by the Jelly builtin Œ¿, which gets the 1-based index of the permutation.

Kevin Cruijssen

Posted 2020-02-22T22:06:25.107

Reputation: 67 575

Oooh we have an nth-permutation built-in, why did i never notice that! Though I guess it doesn't actually save any bytes over œ + è... – Grimmy – 2020-02-24T13:09:24.070

1@Grimmy Hmm, seeing your comment actually made me realize that œ...è is a byte shorter here, since .I doesn't work on strings (only on lists). So I can drop the S. – Kevin Cruijssen – 2020-02-24T13:11:45.707

2

Pure Bash, 170 bytes

a=`echo {A..Z}`')!@#$%^&*("+,-./:[\]`'
b=`echo {a..z}`0123456789"'=<_>?;{|}~"
c=$a$b
d=$b$a
for((;n<164;n++));do [ "$1" = "${c:n:1}" ]&&echo ${d:n:1}&&exit;done
echo "$1"

Try it online!

I decided to implement a solution inspired by @Noodle9's very nice answer, but using pure bash without any utilities (like tr).

Note that you need to run this in an empty directory; this can be fixed at the cost of 2 bytes by using quotes for "{d:n:1}". (This issue only arises with a non-empty current directory when the input is 8, so the output is supposed to be *.)

On the other hand, you can gain 2 bytes if you don't require output for the input \n: just eliminate the quotes in the final echo.

Mitchell Spector

Posted 2020-02-22T22:06:25.107

Reputation: 3 392

You save 3 bytes using echo {a..z} {0..9} like this.

– Noodle9 – 2020-02-27T08:56:21.583

@Noodle9 Thanks for the suggestion. I had tried that before posting, but it was a little bit longer that way. (The problem is that {0..9} expands to 0 1 2 3 4 5 6 7 8 9, so I needed to add spaces into the other string to make the two strings still match up correctly.) But maybe you figured out some clever way of dealing with that? (Your link looks like it's just a link to your original answer, not to whatever you intended to link to.) – Mitchell Spector – 2020-02-27T19:49:52.137

Sorry I sent the wrong link. I didn't perform any magic, simply substituted {0-9} for 0123456789 and it worked for your test. So incorrect then. :-( – Noodle9 – 2020-02-27T23:01:44.000

@Noodle9 That's a shame -- I was hoping you had come up with some cool trick for getting past that issue! (If it worked for you on some test, you must have gotten lucky with the particular input you happened to try.) – Mitchell Spector – 2020-02-27T23:10:33.533

@Noodle9 Unfortunately, that doesn't work :( . If you look at the test in your link, you'll see that, for instance, b gets mapped to b, instead of to B. I investigated it a bit when I was working on this answer, and it turns out that the {A..Z} construct isn't expanded on the right side of an assignment statement. – Mitchell Spector – 2020-02-27T23:59:29.037

Yes, just noticed letters aren't working. – Noodle9 – 2020-02-28T00:00:10.457

166 3rd time lucky! :D – Noodle9 – 2020-02-28T00:01:25.090

@Noodle9 That was actually what I mentioned in the last paragraph of the answer -- "On the other hand, you can gain 2 bytes if you don't require output for the input \n: just eliminate the quotes in the final echo." You can do this, but then it doesn't have the right output for input \n. Normally the output is the intended output character followed by a \n, but if you do this, then the output for \n is just \n, instead of \n\n. I felt that was at best borderline acceptable, so I didn't do it. – Mitchell Spector – 2020-02-28T00:07:50.907

@Noodle9 But thanks for looking at it! – Mitchell Spector – 2020-02-28T00:09:26.970

@Noodle9 I just noticed that you also changed "$1" to $1 in the for loop. But unfortunately that won't work if the input is a space, for instance. – Mitchell Spector – 2020-02-28T00:14:55.890

Ah, this reminds me of writing scripts that'll process every weird filename possible! :-) – Noodle9 – 2020-02-28T00:20:18.413

I know -- avoiding weird characters is a real nuisance with bash. But that's really only because it's so flexible, so I can't complain. – Mitchell Spector – 2020-02-28T00:23:03.023

True, bash is amazingly powerful working on the command line where everything is a stream of characters that process streams of characters. Love it! :-) – Noodle9 – 2020-02-28T00:25:35.410

I agree :). @Noodle9 – Mitchell Spector – 2020-02-28T00:37:46.723

1

APL (Dyalog Extended), 60 bytesSBCS

Anonymous tacit prefix function.

-(,⊃⍨⍳-¯1*⍳)⍨('"''+=,<-_.>/?:;[{\|]}`~',∊')!@#$%^&*(',¨⎕D),-

Try it online!

- swap case

(), prepend the following:

⎕D the digits

')!@#$%^&*(',¨ prepend each of these characters to the corresponding digit

ϵnlist (flatten)

'"''+=,<-_.>/?:;[{\|]}`~', prepend these characters ('' is an escaped single quote)

-()⍨ apply the following function with that as left argument and the case-swapped original argument as right argument:

ɩndex of the case-swapped argument in the substitution string

¯1* negative one raised to that power

⍳- subtract that from the ɩndex of the case-swapped argument in the substitution string

,⊃⍨ use that to pick an element from the case-swapped argument appended to the substitution string

Adám

Posted 2020-02-22T22:06:25.107

Reputation: 37 779

1

Pure Bash, 217 bytes

P=printf\ 
a=`$P%x "'$1"`
case $a in 3[ab])x=1;;2[27])x=5;;2[68]|3[79])x=17;;2a|38)x=18;;2b|3d)x=22;;29|30)x=25;;36|5e)x=104;;2d|32|40|5f)x=114;;20|7f)x=0;;60|7e)x=30;;[23]?)x=16;;[4-7]?)x=32
esac
$P\\`$P%o $[0x$a^x]`

Try it online!

Just bash -- no Unix utilities.

Input character is passed as an argument, and the output is on stdout.

_____________________________________________________________

Test runs:

$ cat shifttoggle
P=printf\ 
a=`$P%x "'$1"`
case $a in 3[ab])x=1;;2[27])x=5;;2[68]|3[79])x=17;;2a|38)x=18;;2b|3d)x=22;;29|30)x=25;;36|5e)x=104;;2d|32|40|5f)x=114;;20|7f)x=0;;60|7e)x=30;;[23]?)x=16;;[4-7]?)x=32
esac
$P\\`$P%o $[0x$a^x]`


$cat shifttoggleTest
#!/bin/bash

count=1

for s in ')!@#$%^&*("+,-./:' "0123456789'=<_>?;" ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz '[\]`' '{|}~' $' \a\n\r\t\x7F\x80\x88\x8F'
  do
    echo "TEST #$count"

    for ((n=0;n<${#s};n++))
      do
        c="${s:$n:1}"
        d=`./shifttoggle "$c"`

    # Backticks remove any trailing newline, so restore that here for the case where c is \n (you can check that shifttoggle really prints \n in that case).
    if test -z "$d"
          then
            d=$'\n'
        fi

    if test "$count" -eq 7
          then
            printf "  ASCII %d => ASCII %d\n" "'$c" "'$d"
      else
            echo "  $c => $d"
        fi

      done

      (( count++ ))
      echo ''
  done


$ ./shifttoggleTest 
TEST #1
  ) => 0
  ! => 1
  @ => 2
  # => 3
  $ => 4
  % => 5
  ^ => 6
  & => 7
  * => 8
  ( => 9
  " => '
  + => =
  , => <
  - => _
  . => >
  / => ?
  : => ;

TEST #2
  0 => )
  1 => !
  2 => @
  3 => #
  4 => $
  5 => %
  6 => ^
  7 => &
  8 => *
  9 => (
  ' => "
  = => +
  < => ,
  _ => -
  > => .
  ? => /
  ; => :

TEST #3
  A => a
  B => b
  C => c
  D => d
  E => e
  F => f
  G => g
  H => h
  I => i
  J => j
  K => k
  L => l
  M => m
  N => n
  O => o
  P => p
  Q => q
  R => r
  S => s
  T => t
  U => u
  V => v
  W => w
  X => x
  Y => y
  Z => z

TEST #4
  a => A
  b => B
  c => C
  d => D
  e => E
  f => F
  g => G
  h => H
  i => I
  j => J
  k => K
  l => L
  m => M
  n => N
  o => O
  p => P
  q => Q
  r => R
  s => S
  t => T
  u => U
  v => V
  w => W
  x => X
  y => Y
  z => Z

TEST #5
  [ => {
  \ => |
  ] => }
  ` => ~

TEST #6
  { => [
  | => \
  } => ]
  ~ => `

TEST #7
  ASCII 32 => ASCII 32
  ASCII 7 => ASCII 7
  ASCII 10 => ASCII 10
  ASCII 13 => ASCII 13
  ASCII 9 => ASCII 9
  ASCII 127 => ASCII 127
  ASCII 128 => ASCII 128
  ASCII 136 => ASCII 136
  ASCII 143 => ASCII 143

Mitchell Spector

Posted 2020-02-22T22:06:25.107

Reputation: 3 392

1

C (gcc), 100 bytes

char*strchrnul(),*s=")0@2^6&7*8(9\"'+=-_:;`~\0";f(c){c=s[strchrnul(s,c)-s^1]?:(c%127>32)<<4+c/64^c;}

Similar to the Javascript strategy. I'd like to get rid of the extra parentheses but this is pretty good.

Try it online!

S.S. Anne

Posted 2020-02-22T22:06:25.107

Reputation: 1 161

1

Ruby -p, 66 bytes

Builds the "lowercase" and "uppercase" strings so that everything lines up, and connects them together to be passed into the tr function so it can do the proper conversions.

l="\\-a-z0-9=[-];',./`"
u='_A-Z)!@#$%^&*(+{|}:"<>?~'
$_.tr!u+l,l+u

Try it online!

Value Ink

Posted 2020-02-22T22:06:25.107

Reputation: 10 608