Who's that Polygon?

14

1

A convenient and useful way to represent topological surfaces is with a fundamental polygon. Each side on a polygon matches to another side and can be either parallel or anti-parallel. For instance the here is the fundamental polygon of a torus:

Torus

To figure out why this is a torus we could imagine our polygon being a sheet of paper. To make the proper surface we want to bend our paper so that the corresponding edges line up with their arrows going the same way. For our torus example we can start by rolling the paper into a cylinder so that the two blue edges (labeled b) are connected. Now we take our tube and bend it so that the two red edges (labeled a) connect to each other. We should have a donut shape, also called the torus.

This can get a bit trickier. If you try to do the same with the following polygon where one of the edges is going in the opposite direction:

Klein Bottle

you might find yourself in some trouble. This is because this polygon represents the Klein bottle which cannot be embedded in three dimensions. Here is a diagram from wikipedia showing how you can fold this polygon into a Klein bottle:

Folding a Klein Bottle


As you may have guessed the task here is to take a fundamental polygon and determine which surface it is. For four sided polygons (the only surfaces you will be required to handle) there are 4 different surfaces.

They are

  • Torus

  • Klein Bottle

  • Sphere

  • Projective plane

Now this is not so I don't expect you to take an image as input instead we will use a convenient notation to represent the fundamental polygon. You may have noticed in the two examples above that I named corresponding edges with the same letter (either a or b), and that I gave the twisted edge an additional mark to show its twisted. If we start at the upper edge and write down the label for each edge as we go clockwise we can get a notation that represents each fundamental polygon.

For example the Torus provided would become abab and the Klein Bottle would become ab-ab. For our challenge we will make it even simpler, instead of marking twisted edges with a negative we will instead make those letters capitalized.

Task

Given a string determine if it represents a fundamental polygon and output a value that corresponding to the proper surface of it is. You do not need to name the surfaces exactly, you just need 4 output distinct values each representing one of the 4 surfaces with a fifth value representing improper input. All of the basic cases are covered in the Simple Tests section, every car will be isomorphic to one of the or invalid.

Rules

  • Sides will not always be labeled with a and b, but they will always be labeled with letters.

  • Valid input will consist of 4 letters, two of one type and two of another. You must always output the correct surface for valid input.

  • You should reject (not output any of the 4 values representing surfaces) invalid input. You may do anything when rejecting an input, as long as it is distinguishable from the 4 surfaces

  • This is so the goal is to minimize the number of bytes in your source code.

Tests

Simple Tests

abab Torus
abAb Klein Bottle
abaB Klein Bottle
abAB Projective Plane
aabb Klein Bottle
aAbb Projective Plane
aabB Projective Plane
aAbB Sphere
abba Klein Bottle
abBa Projective Plane
abbA Projective Plane
abBA Sphere

Trickier Tests

ABAB  Torus
acAc  Klein Bottle
Emme  Projective Plane
zxXZ  Sphere
aaab  Bad input
abca  Bad input
abbaa Bad input
ab1a  Bad input

Post Rock Garf Hunter

Posted 2017-05-20T20:14:19.103

Reputation: 55 382

Why is abab a torus and aabb a Klein bottle? – Neil – 2017-05-20T20:57:23.867

@Neil abab is the example in the first paragraph, you can look there for an explanation. Here is an image showing why aabb is the same as abAb which is a Klein bottle.

– Post Rock Garf Hunter – 2017-05-20T21:00:13.993

Does this have to do with your new language Klein by the way? – Erik the Outgolfer – 2017-05-20T21:29:05.140

"...but they will always be labeled with letters" what does this add to the challenge, why not allow any base four representation as input? – Jonathan Allan – 2017-05-20T21:31:39.853

@JonathanAllan I'm not sure what you mean by base four representation. I chose letters because other symbols usually don't have upper and lower case versions. – Post Rock Garf Hunter – 2017-05-20T21:38:15.860

You have a representation for which valid input is a subset of the four digit base four numbers (i.e. your digits are a, b, A, and B). – Jonathan Allan – 2017-05-20T21:40:40.247

@JonathanAllan I don't think I would want to do that. The challenge is really an input processing challenge, there is not a lot of actual math going on behind the scenes. I think turning it into a base 4 challenge would make it too simple. The challenge is pretty simple as is. – Post Rock Garf Hunter – 2017-05-20T21:59:15.833

Well I have not worked out all the rules, but maybe I am simple :p – Jonathan Allan – 2017-05-20T22:07:50.017

1What bad inputs do we have to handle and identify as bad? All possible strings? Printable ASCII? Any limits on length? If we write a function, might it be passed a non-string object? Really, this whole input processing business strikes me as a chameleon challenge. – xnor – 2017-05-21T07:29:33.290

@xnor You will only receive strings other than that they may contain anything. Don't get me wrong this challenge is an input processing challenge. Any reasonable solution will do very little actual math. – Post Rock Garf Hunter – 2017-05-21T07:35:54.547

1@WheatWizard In that case, can you please make that clear in the title and body? It reads like math all the way until Rules, and even there it's easy to miss the gamechanging requirement to validate rather than just classify. – xnor – 2017-05-21T07:39:38.867

2Separately, I think an explanation is missing of what makes a string be classified into a given category, since you seem not to expect people to do out the math behind the classification. I think I could puzzle out the rules from test cases, but that's far from ideal. – xnor – 2017-05-21T07:43:05.157

@xnor I've hopefully made it clear it's a input processing challenge if it's not feel free to inform me or edit it yourself. However I can't implement your second suggestion at this moment. I assure you every possible shape is covered in the simple cases section with the only variation being choice of letter or inferring the case on a pair, but I'll try to make it clear later. – Post Rock Garf Hunter – 2017-05-21T07:48:43.203

so a sphere is a polygon? – Khaled.K – 2017-05-22T06:27:31.263

Answers

6

Retina, 123 bytes

i`(.)(\1..)
$2$1
iG`^([a-z])(?!\1)([a-z])(\1\2|\2\1)$
(..)\1
T
.*(.).\1.*|(.)(.)\3\2
B
(.)..\1|.(.)\2.
P
i`(.)..\1
S
....
P

Try it online! Thanks to @JonathanAllen for pointing out a bug in my code and also how to save some bytes; I also golfed some more bytes myself so I can't credit him for a specific figure. Explanation:

i`(.)(\1..)
$2$1

If the first two letters are the same (ignoring case), move the first letter to be fourth. This reduces the number of cases that I need to test.

iG`^([a-z])(?!\1)([a-z])(\1\2|\2\1)$

If there are not exactly four letters, or the first two letters are the same, or the last two letters do not duplicate the first two, then delete everything.

(..)\1
T

The torus is the easy case: a pair of letters, repeated matching case.

.*(.).\1.*|(.)(.)\3\2
B

If one of the pair matches case (in which case other of the pair must mismatch case), then that is a Klein bottle. Alternatively, if the pair matches case but is reversed, then it is also a Klein bottle.

(.)..\1|.(.)\2.
P

If on the other hand the pair is reversed, but only one of the pair matches case, then that is a projective plane.

i`(.)..\1
S

And if the pair is reversed but neither matches case, then that is a sphere. (i`.(.)\1. would also work.)

....
P

Everything else is a projective plane.

Neil

Posted 2017-05-20T20:14:19.103

Reputation: 95 035

1@JonathanAllan Thanks for the tip; hopefully this version has better validation. – Neil – 2017-05-21T00:03:44.790

If only I could work out the logic myself :p – Jonathan Allan – 2017-05-21T00:05:23.057

1

Jelly, 52 51 58 bytes

+7 bytes, I found that the mapping I'd used did not work for some (off example) case change scenarios.

“nḲ⁾⁶ƥ¦ṃṗḋ’b4s4‘µṙJ;U$µ€
,ŒsṢÞṪµŒlĠL€⁼2,2ȧ⁸i@€ṢeЀ¢TṪ’:3,8

A monadic link taking a string and returning the following five distinct, consistent values:

  • [-1,-1] - invalid input
  • [0,0] - projective plane
  • [1,0] - Klein bottle
  • [2,0] - sphere
  • [2,1] - torus

Try it online! or see the test suite.

How?

Any fundamental polygon is:

  • unchanged under rotation - one may turn the paper like a steering wheel
  • unchanged under reflection - one may turn the paper over
  • unchanged under case reversal - one may swap as and As and/or swap bs and Bs with no effect - since we want to match the directions the actual label is inconsequential.

As such there are nine equivalence classes. The code creates lists of four integers each representing an example of one of the nine equivalence classes, creates the four rotations of each, reflects each of those and then checks if a translated form of the input exists in each list. The classes are ordered P,P,P,K,K,K,S,S,T, so taking the 0-based index integer divided by each of [3,8] yields the four valid outputs (indexing is 1-based and the atom e returns 0 for non-existence, so subtracting 1 and integer dividing by each of [3,8] yields [-1,-1] for the invalid case).

“nḲ⁾⁶ƥ¦ṃṗḋ’b4s4‘µṙJ;U$µ€ - Link 1, symmetries of the 9 equivalence classes: no arguments
“nḲ⁾⁶ƥ¦ṃṗḋ’              - base 250 number                 1704624888339951310984
           b4            - convert to base 4               [1,1,3,0,1,2,2,0,1,2,3,0,0,2,2,0,1,3,1,0,2,1,2,0,2,3,1,0,3,1,2,0,2,0,2,0]
             s4          - split into 4s                   [[1,1,3,0],[1,2,2,0],[1,2,3,0],[0,2,2,0],[1,3,1,0],[2,1,2,0],[2,3,1,0],[3,1,2,0],[2,0,2,0]]
               ‘         - increment (vectorises)          [[2,2,4,1],[2,3,3,1],[2,3,4,1],[1,3,3,1],[2,4,2,1],[3,2,3,1],[3,4,2,1],[4,2,3,1],[3,1,3,1]]
                µ     µ€ - for €ach:                       ...e.g. [2,2,4,1]:
                  J      -   range of length               [1,2,3,4]
                 ṙ       -   rotate left by (vectorises)   [[2,4,1,2],[4,1,2,2],[1,2,2,4],[2,2,4,1]]
                     $   -   last two links as a monad:
                    U    -     upend (reverse each)        [[2,1,4,2],[2,2,1,4],[4,2,2,1],[1,4,2,2]]
                   ;     -     concatenate                 [[2,4,1,2],[4,1,2,2],[1,2,2,4],[2,2,4,1],[2,1,4,2],[2,2,1,4],[4,2,2,1],[1,4,2,2]]

,ŒsṢÞṪµŒlĠL€⁼2,2ȧ⁸i@€ṢeЀ¢TṪ’:3,8 - Main link: string s      e.g. "xOxO"
 Œs                               - swap case                     "XoXo"
,                                 - pair with s                   ["xOxO","XoXo"]
    Þ                             - sort by:
   Ṣ                              -   sort                        ["xOxO","XoXo"]
     Ṫ                            - tail                          "XoXo"
      µ                           - monadic chain separation, call that v
       Œl                         - convert to lowercase          "xoxo"
         Ġ                        - group indices by value        [[2,4],[1,3]]
          L€                      - length of each                [2,2]
             2,2                  - 2 pair 2                      [2,2]
            ⁼                     - equal? (1 if so, 0 if not)    1
                 ⁸                - link's left argument, v       "XoXo"
                ȧ                 - and (v if equal, 0 if not)    "XoXo"
                     Ṣ            - sort v                        "XoXo"
                  i@€             - first index for €ach          [1,3,1,3]
                         ¢        - call the last link (1) as a nilad
                       Ѐ         - map over right:
                      e           -   exists in?                  [0,0,0,0,0,0,0,0,1]
                          T       - truthy indexes                [                9]
                           Ṫ      - tail (empty list yields 0)    9
                            ’     - decrement                     8
                              3,8 - 3 pair 8                      [3,8]
                             :    - integer division (vectorises) [2,1]

Note: 11 bytes (ŒlĠL€⁼2,2ȧ⁸) only validate the input string as being of the correct form - without this code every example case passes except that ab1a is evaluated as if it were abBa, a projective plane.

Jonathan Allan

Posted 2017-05-20T20:14:19.103

Reputation: 67 804