mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	* sample/trick2015/: added the award-winning entries of TRICK 2015.
See https://github.com/tric/trick2015 for the contest outline. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@53041 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
		
							parent
							
								
									d65bc80d3f
								
							
						
					
					
						commit
						5c28308f9f
					
				
					 23 changed files with 881 additions and 0 deletions
				
			
		|  | @ -1,3 +1,8 @@ | |||
| Fri Dec 11 23:33:40 2015  Yusuke Endoh  <mame@ruby-lang.org> | ||||
| 
 | ||||
| 	* sample/trick2015/: added the award-winning entries of TRICK 2015. | ||||
| 	  See https://github.com/tric/trick2015 for the contest outline. | ||||
| 
 | ||||
| Fri Dec 11 17:59:05 2015  Eric Wong  <e@80x24.org> | ||||
| 
 | ||||
| 	* insns.def (opt_case_dispatch): avoid converting Infinity | ||||
|  |  | |||
|  | @ -8,6 +8,8 @@ THESE ARE BAD EXAMPLES!  You must NOT use them as a sample code. | |||
| * shinh/entry.rb: "Most Readable" - Silver award | ||||
| * yhara/entry.rb: "Worst abuse of constants" - Dishonorable mention | ||||
| 
 | ||||
| These files are licensed under MIT license. | ||||
| 
 | ||||
| For the contest outline and other winning entries, see: | ||||
| 
 | ||||
| https://github.com/tric/trick2013 | ||||
|  |  | |||
							
								
								
									
										16
									
								
								sample/trick2015/README.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								sample/trick2015/README.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | |||
| This directory contains the award-winning entries of | ||||
| the 2nd Transcendental Ruby Imbroglio Contest for rubyKaigi (TRICK 2015). | ||||
| 
 | ||||
| THESE ARE BAD EXAMPLES!  You must NOT use them as a sample code. | ||||
| 
 | ||||
| * kinaba/entry.rb: "Best piphilology" - **Gold award** | ||||
| * ksk\_1/entry.rb: "Most unreadable ALU" - **Silver award** | ||||
| * monae/entry.rb: "Doubling amphisbaena award" - **Bronze award** | ||||
| * eregon/entry.rb: "Least general solver" - 4th prize | ||||
| * ksk\_2/entry.rb: "Most general solver" - 5th prize | ||||
| 
 | ||||
| These files are licensed under MIT license. | ||||
| 
 | ||||
| For the contest outline and other winning entries, see: | ||||
| 
 | ||||
| https://github.com/tric/trick2015 | ||||
							
								
								
									
										3
									
								
								sample/trick2015/eregon/authors.markdown
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								sample/trick2015/eregon/authors.markdown
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | |||
| * Benoit Daloze (eregon) | ||||
|   * eregontp@gmail.com | ||||
|   * cctld: be | ||||
							
								
								
									
										16
									
								
								sample/trick2015/eregon/entry.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								sample/trick2015/eregon/entry.rb
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | |||
| class String;def[]*a;$*<<a;b;end;end; | ||||
| _=0;z="C=Fiber;s=$*;a=*0..8;l=C.new{e | ||||
| xit},*a.product(a).select{|r,c|s[r][c | ||||
| ]==0}."[1,9,_, _,_,8, _,_,5]+"map{|r, | ||||
| c|C.ne"[_,_,2, _,5,_, _,8,9]+"w{o=s[r | ||||
| ][c];l"[8,_,6, 7,4,_, _,_,_]+"oop{(1. | ||||
| .9).map{|n|C.yield(s[r][c]=n)if a.non | ||||
| e?{|k|"[_,_,_, _,_,4, _,9,2]+"s[r][k] | ||||
| ==n||s"[_,2,3, _,7,_, 8,1,_]+"[k][c]= | ||||
| =n||s["[5,6,_, 8,_,_, _,_,_]+"r-r%3+k | ||||
| %3][c-c%3+k/3]==n}};s[r][c]=o;C.yield | ||||
| }}},C."[_,_,_, _,2,7, 9,_,3]+"new{loo | ||||
| p{puts"[9,3,_, _,8,_, 1,_,_]+" s.map{ | ||||
| |r|r*'"[2,_,_, 5,_,_, _,4,8]+" '}<<'' | ||||
| ;C.yield}};c=l[i=1];loop{c=l[i+=c.res | ||||
| ume ? 1:-1]}";eval z.tr ?\n,'' | ||||
							
								
								
									
										70
									
								
								sample/trick2015/eregon/remarks.markdown
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								sample/trick2015/eregon/remarks.markdown
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,70 @@ | |||
| ### Remarks | ||||
| 
 | ||||
| Just run it without arguments: | ||||
| 
 | ||||
|     ruby entry.rb | ||||
| 
 | ||||
| I confirmed the following implementations and platforms: | ||||
| 
 | ||||
| * Linux: | ||||
|   * ruby 2.3.0dev (2015-10-30 trunk 52394) [x86\_64-linux] | ||||
|   * ruby 2.2.2p95 (2015-04-13 revision 50295) [x86\_64-linux] | ||||
|   * ruby 2.0.0p647 (2015-08-18) [x86\_64-linux] | ||||
| * Darwin: | ||||
|   * ruby 2.0.0p247 (2013-06-27 revision 41674) [x86\_64-darwin10.8.0] | ||||
|   * jruby 9.0.3.0 (2.2.2) 2015-10-21 633c9aa Java HotSpot(TM) 64-Bit Server VM 25.11-b03 on 1.8.0\_11-b12 +jit [darwin-x86\_64] | ||||
|   * rubinius 2.2.6.n74 (2.1.0 94b3a9b4 2014-03-15 JI) [x86\_64-darwin12.5.0] | ||||
| 
 | ||||
| ### Description | ||||
| 
 | ||||
| This program shows all solutions of any sudoku puzzle. | ||||
| 
 | ||||
| The embedded sudoku puzzle can be changed at wish. | ||||
| 
 | ||||
| Giving an empty puzzle (all `0` or `_`), the program will print every possible completed sudoku puzzle. | ||||
| We do not however make any time guarantee on such behavior. | ||||
| 
 | ||||
| The program is rather small for the task: the solver is actually 302 characters long,   | ||||
| assuming the sudoku puzzle is in a variable `s` and encoded as an array of rows of numbers. | ||||
| 
 | ||||
| ### Internals | ||||
| 
 | ||||
| * The program implements backtracking and keeps state in a very elegant way. | ||||
| * The whole program never goes deeper than 9 stack frames, | ||||
|   but yet can backtrack up to 81 levels! | ||||
| * The main loop of a program is a dance between cells. | ||||
|   On one end is the solutions, on the other the program ends. | ||||
| * The program only uses *infinite* loops and no `break`. | ||||
| * The program interleaves the creation of the solver and the puzzle. | ||||
| * The program is easy to deobfuscate but finding how it works will be more challenging. | ||||
| * The last line contains a smiley. | ||||
| 
 | ||||
| The author likes good numbers: | ||||
| 
 | ||||
|     $ wc entry.rb | ||||
|           15      42     600 | ||||
| 
 | ||||
| The inspiration for this entry comes from: | ||||
| 
 | ||||
| * A newspaper sudoku with multiple solutions | ||||
| * An inspiring paper: `Revisiting Coroutines` | ||||
| 
 | ||||
| Various tricks used for brevity: | ||||
| 
 | ||||
| * The method defined is one of the fews which may contain neither parenthesis nor spaces. | ||||
| * The program uses the return value of Fiber.yield without arguments. | ||||
| * `String#b` is used as a very short `self`. | ||||
| 
 | ||||
| Design issues: | ||||
| 
 | ||||
| * Since `return`-ing from a Fiber is not allowed, the programs must `exit`. | ||||
| * The program reveals that the cartesian product operator is still too long: `a.product(a)` while it could be `a*a`. | ||||
| 
 | ||||
| Note: | ||||
| 
 | ||||
| * In the original code, the last cell was: `C.new{loop{yield s; C.yield}}`, | ||||
|   implementing some sort of "forwarding coroutine". | ||||
| 
 | ||||
| ### Limitation | ||||
| 
 | ||||
| * The program does not want any *argument* with you and will quit quietly if you try some. | ||||
							
								
								
									
										4
									
								
								sample/trick2015/kinaba/authors.markdown
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								sample/trick2015/kinaba/authors.markdown
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | |||
| * kinaba | ||||
|   * twitter.com/kinaba | ||||
|   * kiki@kmonos.net | ||||
|   * cctld: jp | ||||
							
								
								
									
										150
									
								
								sample/trick2015/kinaba/entry.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								sample/trick2015/kinaba/entry.rb
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,150 @@ | |||
| big, temp = Array 100000000**0x04e2 | ||||
| srand big | ||||
| alias $curTerm $initTerm | ||||
| 
 | ||||
| Numeric | ||||
| Interrupt | ||||
| 
 | ||||
| big += big | ||||
| printout _pi_ finish if $never | ||||
| init ||= big | ||||
| $counter ||= 02 | ||||
| 
 | ||||
| private | ||||
| @mainloop | ||||
| while 0x00012345 >= $counter | ||||
| 
 | ||||
|   Rational aprx = 3.141592r | ||||
|   numbase = 0o0000 | ||||
| 
 | ||||
|   @justonce | ||||
|   def increment | ||||
|     $initTerm ||= Integer srand * 0x00000002 | ||||
|     srand $counter += 0x00000001 | ||||
| 
 | ||||
|     $noaction | ||||
|     Integer rand | ||||
|     $noaction | ||||
|     rand | ||||
|     rand | ||||
|     alias $line_cnt $. | ||||
|   end | ||||
| 
 | ||||
|   @lets_just | ||||
|   @assume | ||||
|   diameter = 100000 | ||||
| 
 | ||||
|   @you | ||||
|   @then_have | ||||
|   permtr |= +3_14159 | ||||
| 
 | ||||
|   return if $nomeaning | ||||
| 
 | ||||
|   @onlyuse | ||||
|   increment | ||||
| 
 | ||||
|   beautiful computer action if $nothing | ||||
|   $sigmaTerm ||= init | ||||
|   $curTerm /= srand and init | ||||
|   pi, = Integer $sigmaTerm unless $nomean | ||||
| 
 | ||||
|   iterator? | ||||
|   $counter += 1 | ||||
|   atan real_one multiplied by__four unless | ||||
|   srand +big && $counter >> 0b1 | ||||
| 
 | ||||
|   Enumerable | ||||
|   Fixnum | ||||
|   Bignum | ||||
|   Math | ||||
|   Complex | ||||
|   Comparable | ||||
|   TrueClass | ||||
|   Dir | ||||
|   Encoding | ||||
|   Data | ||||
|   Hash | ||||
|   Method | ||||
|   Enumerator | ||||
|   Exception | ||||
|   Fiber | ||||
|   Errno | ||||
|   FalseClass | ||||
|   Mutex | ||||
|   NilClass | ||||
|   IO | ||||
|   GC | ||||
| 
 | ||||
|   num = numbase |= srand | ||||
| 
 | ||||
|   ENV | ||||
|   Float | ||||
|   MatchData | ||||
|   Proc | ||||
|   TracePoint | ||||
|   KeyError | ||||
|     p   or | ||||
|   FileTest | ||||
|   File | ||||
|   EOFError | ||||
|     p | ||||
|     p | ||||
|     p | ||||
|   Binding | ||||
|   Time | ||||
|   Class | ||||
| 
 | ||||
|   $sigmaTerm += $curTerm | ||||
|   puts a HelloWorld if $nomean | ||||
|   SystemExit | ||||
| 
 | ||||
|   !LoadError | ||||
|   31i | ||||
|   3.1415e0 | ||||
|   Array 14 + 3 | ||||
|   IndexError | ||||
|   Range | ||||
|   false | ||||
|   55555 | ||||
|   NameError | ||||
| 
 | ||||
|   Object | ||||
|   @ori | ||||
|     @ent | ||||
|   RubyVM | ||||
| 
 | ||||
|   pi += 3_3_1_3_8 | ||||
| 
 | ||||
|   @use | ||||
|   @lots_of | ||||
|   @keywords | ||||
|   begin | ||||
|     self | ||||
|     $noaction | ||||
|     not $important | ||||
|     nil | ||||
|     __FILE__.object_id | ||||
|   rescue | ||||
|     next | ||||
|     redo if __LINE__ | ||||
|     defined? +$nomeaning | ||||
|     $noaction | ||||
|     $nomean | ||||
|     break $never | ||||
|   ensure | ||||
|     class PiCompute | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   This code cannot _ be executed with typical style unless true | ||||
|   $curTerm *= num | ||||
| end | ||||
| 
 | ||||
| @ll_set | ||||
| @re_U_ok | ||||
| 
 | ||||
| $Enjoy | ||||
| $Superb | ||||
| $TRICK15 and a number | ||||
| 
 | ||||
| print pi | ||||
							
								
								
									
										85
									
								
								sample/trick2015/kinaba/remarks.markdown
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								sample/trick2015/kinaba/remarks.markdown
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,85 @@ | |||
| ### Remarks | ||||
| 
 | ||||
| Just run it with no argument: | ||||
| 
 | ||||
|     $ ruby entry.rb | ||||
| 
 | ||||
| I confirmed the following implementation/platform: | ||||
| 
 | ||||
| - ruby 2.2.3p173 (2015-08-18 revision 51636) [x64-mingw32] | ||||
| 
 | ||||
| 
 | ||||
| ### Description | ||||
| 
 | ||||
| The program is a [Piphilology](https://en.wikipedia.org/wiki/Piphilology#Examples_in_English) | ||||
| suitable for Rubyists to memorize the digits of [Pi](https://en.wikipedia.org/wiki/Pi). | ||||
| 
 | ||||
| In English, the poems for memorizing Pi start with a word consisting of 3-letters, | ||||
| 1-letter, 4-letters, 1-letter, 5-letters, ... and so on. 10-letter words are used for the | ||||
| digit `0`. In Ruby, the lengths of the lexical tokens tell you the number. | ||||
| 
 | ||||
|     $ ruby -r ripper -e \ | ||||
|         'puts Ripper.tokenize(STDIN).grep(/\S/).map{|t|t.size%10}.join' < entry.rb | ||||
|     31415926535897932384626433832795028841971693993751058209749445923078164062862... | ||||
| 
 | ||||
| The program also tells you the first 10000 digits of Pi, by running. | ||||
| 
 | ||||
|     $ ruby entry.rb | ||||
|     31415926535897932384626433832795028841971693993751058209749445923078164062862... | ||||
| 
 | ||||
| 
 | ||||
| ### Internals | ||||
| 
 | ||||
| Random notes on what you might think interesting: | ||||
| 
 | ||||
| - The 10000 digits output of Pi is seriously computed with no cheets. It is calculated | ||||
|   by the formula `Pi/2 = 1 + 1/3 + 1/3*2/5 + 1/3*2/5*3/7 + 1/3*2/5*3/7*4/9 + ...`. | ||||
| 
 | ||||
| - Lexical tokens are not just space-separated units. For instance, `a*b + cdef` does | ||||
|   not represent [3,1,4]; rather it's [1,1,1,1,4]. The token length | ||||
|   burden imposes hard constraints on what we can write. | ||||
| 
 | ||||
| - That said, Pi is [believed](https://en.wikipedia.org/wiki/Normal_number) to contain | ||||
|   all digit sequences in it. If so, you can find any program inside Pi in theory. | ||||
|   In practice it isn't that easy particularly under the TRICK's 4096-char | ||||
|   limit rule. Suppose we want to embed `g += hij`. We have to find [1,2,3] from Pi. | ||||
|   Assuming uniform distribution, it occurs once in 1000 digits, which already consumes | ||||
|   5000 chars in average to reach the point. We need some TRICK. | ||||
| 
 | ||||
|   - `alias` of global variables was useful. It allows me to access the same value from | ||||
|     different token-length positions. | ||||
| 
 | ||||
|   - `srand` was amazingly useful. Since it returns the "previous seed", the token-length | ||||
|     `5` essentially becomes a value-store that can be written without waiting for the | ||||
|     1-letter token `=`. | ||||
| 
 | ||||
| - Combination of these techniques leads to a carefully chosen 77-token Pi computation | ||||
|   program (quoted below), which is embeddable to the first 242 tokens of Pi. | ||||
|   Though the remaining 165 tokens are just no-op fillers, it's not so bad compared to | ||||
|   the 1000/3 = 333x blowup mentioned above. | ||||
| 
 | ||||
| 
 | ||||
|     big, temp = Array 100000000**0x04e2 | ||||
|     srand big | ||||
|     alias $curTerm $initTerm | ||||
|     big += big | ||||
|     init ||= big | ||||
|     $counter ||= 02 | ||||
|     while 0x00012345 >= $counter | ||||
|       numbase = 0x0000 | ||||
|       $initTerm ||= Integer srand * 0x00000002 | ||||
|       srand $counter += 0x00000001 | ||||
|       $sigmaTerm ||= init | ||||
|       $curTerm /= srand | ||||
|       pi, = Integer $sigmaTerm | ||||
|       $counter += 1 | ||||
|       srand +big && $counter >> 0b1 | ||||
|       num = numbase |= srand | ||||
|       $sigmaTerm += $curTerm | ||||
|       pi += 3_3_1_3_8 | ||||
|       $curTerm *= num | ||||
|     end | ||||
|     print pi | ||||
| 
 | ||||
| - By the way, what's the blowup ratio of the final code, then? | ||||
|   It's 242/77, whose first three digits are, of course, 3.14. | ||||
							
								
								
									
										3
									
								
								sample/trick2015/ksk_1/authors.markdown
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								sample/trick2015/ksk_1/authors.markdown
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | |||
| * Keisuke Nakano | ||||
|   * ksk@github, ksknac@twitter | ||||
|   * cctld: jp | ||||
							
								
								
									
										1
									
								
								sample/trick2015/ksk_1/entry.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								sample/trick2015/ksk_1/entry.rb
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | |||
| %%%while eval '_=%%r%%(.)...\1=%%=~[%%%%,,,,,%%%s  ?=]*%%%%%%#"]*%%%%3x+1?%%'.% %%",%*p(_||=eval($**%%%)) | ||||
							
								
								
									
										120
									
								
								sample/trick2015/ksk_1/remarks.markdown
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								sample/trick2015/ksk_1/remarks.markdown
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,120 @@ | |||
| ### Remarks | ||||
| 
 | ||||
| The program is run with a positive integer as an argument, e.g., | ||||
| ```shell | ||||
|   ruby entry.rb 27 | ||||
| ``` | ||||
| It has been confirmed to be run on | ||||
| ``` | ||||
|   ruby 1.9.3p385 (2013-02-06 revision 39114) [x86_64-darwin11.4.2] | ||||
|   ruby 2.0.0p481 (2014-05-08 revision 45883) [universal.x86_64-darwin13] | ||||
|   ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-linux] | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| ### Description | ||||
| 
 | ||||
| The program prints a Collatz sequence started with a given number, | ||||
| that is, it repeatedly outputs numbers obtained by applying the | ||||
| following Half-Or-Triple-Plus-One (HOTPO) process to the previous | ||||
| number: | ||||
| 
 | ||||
| > If the number is even, divide it by two, otherwise, multiply it by three and add one. | ||||
| 
 | ||||
| until the number becomes 1.  Collatz conjectured that no matter from | ||||
| the process starts it always eventually terminates.  This is still | ||||
| an open problem, hence the program may not terminate for some | ||||
| numbers.  It is known that there is no such exception below 2<sup>60</sup>. | ||||
| 
 | ||||
| 
 | ||||
| ### Internals | ||||
| 
 | ||||
| The source code does not contain either conditional branch or arithmetic operation. | ||||
| The trick shall be revealed step by step. | ||||
| 
 | ||||
| First, the code is obfuscated by using `%`-notations, | ||||
| `*`(String#join), `%`-formatting, restructuring, and so on. | ||||
| Here is an equivalent readable program: | ||||
| ```ruby | ||||
| n = ARGV[0].to_i | ||||
| begin  | ||||
|   # do nothing | ||||
| end while begin | ||||
|   puts n | ||||
|   n = (/(.)...\1=/ =~ eval('[",,,,,"'+ '",'*n + '  ?=].join#"].join("3x+1?")')) | ||||
| end | ||||
| ``` | ||||
| The line  | ||||
| ```ruby | ||||
|   n = (/(.)...\1=/ =~ eval('[",,,,,"'+ '",'*n + '  ?=].join#"].join("3x+1?")')) | ||||
| ``` | ||||
| performs the HOTPO process. | ||||
| The `eval` expression here returns a string as explained in detail later. | ||||
| Since *regex*`=~`*str* returns index of first match of *regex* in *str*, | ||||
| the regular expression `(.)...\1` must match the string | ||||
| at index `n/2` if `n` is even and | ||||
| at `3*n+1` if `n` is odd greater than 1. | ||||
| The match must fail in the case of `n = 1` so that it returns `nil`. | ||||
| 
 | ||||
| The key of simulating the even-odd conditional branch on `n` in the | ||||
| HOTPO process is an `n`-length sequence of the incomplete fragments | ||||
| `",` where the double-quote `"` changes its role of opening/closing | ||||
| string literals alternately.  If `n` is even, the string in the `eval` | ||||
| expression is evaluated as | ||||
| ```ruby | ||||
|   => '[",,,,,"'+ '",' + '",' + '",' + ... + '",' + '  ?=].join#...' | ||||
|   => '[",,,,,"",",",...",  ?=].join#...' | ||||
| ``` | ||||
| where the last double-quote `"` is closing hence the code after `#` is | ||||
| ignored as comments.  Note that `"ab""cd"` in Ruby is equivalent to | ||||
| `"abcd"`.  Therefore the `eval` expression is evaluated into | ||||
| ```ruby | ||||
|   ",,,,,...,=" | ||||
| ``` | ||||
| where the number of commas is `5+n/2`. | ||||
| As a result, the regular expression `(.)...\1=` matches `,,,,,=` | ||||
| at the end of string, that is, at index `5+n/2-5 = n/2`. | ||||
| 
 | ||||
| If `n` is odd, the string in the `eval` expression is evaluated as | ||||
| ```ruby | ||||
|   => '[",,,,,"'+ '",' + '",' + '",' + '",' + ... + '",' + '  ?=].join#"].join("3x+1?")' | ||||
|   => '[",,,,,"",",",",...,",  ?=].join#"].join("3x+1?")' | ||||
| ``` | ||||
| where the last element in the array is `", ?=].join#"`.  Threfore the | ||||
| `eval` expression is evaluated into  | ||||
| ``` | ||||
|   ",,,,,,3x+1?,3x+1?,...,3x+1?,  ?=].join#" | ||||
| ``` | ||||
| where the number of `,3x+1?` is `(n-1)/2`.  As a result, the regular | ||||
| expression `(.)...\1=` matches `?,  ?=` at the almost end of string, | ||||
| that is, at index `5+(n-1)/2*6-1 = 3n+1`, provided that the match | ||||
| fails in the case of `n = 1` because the symbol `?` occurs only once | ||||
| in the string. | ||||
| 
 | ||||
| One may notice that the string `3x+1` in the code could be other | ||||
| four-character words.  I chose it because the Collatz conjecture is | ||||
| also called the 3x+1 problem. | ||||
| 
 | ||||
| 
 | ||||
| ### Variant | ||||
| 
 | ||||
| The Collatz conjecture is equivalently stated as, | ||||
| 
 | ||||
| > no matter from the HOTPO process starts, it always eventually | ||||
|   reaches the cycle of 4, 2, and 1 | ||||
| 
 | ||||
| instead of termination of the process at 1.  This alternative | ||||
| statement makes the program simpler because we do not have to care the | ||||
| case of n = 1.  It can be obtained by replacing the regular expression | ||||
| is simply `/=/` and removing a padding `",,,,,"`.  The program no | ||||
| longer terminates, though. | ||||
| 
 | ||||
| 
 | ||||
| ### Limination | ||||
| 
 | ||||
| The implementation requires to manipulate long strings even for some | ||||
| small starting numbers.  For example, starting from 1,819, the number | ||||
| will reach up to 1,276,936 which causes SystemStackError on Ruby 1.9.3. | ||||
| The program works on Ruby 2.0.0 and 2.2.3, though. | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										6
									
								
								sample/trick2015/ksk_2/abnormal.cnf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								sample/trick2015/ksk_2/abnormal.cnf
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | |||
| c Example CNF format file | ||||
| c | ||||
| p cnf 4 3 | ||||
| 1 3 -4 0 | ||||
| 4 0 2 | ||||
| -3 | ||||
							
								
								
									
										3
									
								
								sample/trick2015/ksk_2/authors.markdown
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								sample/trick2015/ksk_2/authors.markdown
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | |||
| * Keisuke Nakano | ||||
|   * ksk@github, ksknac@twitter | ||||
|   * cctld: jp | ||||
							
								
								
									
										1
									
								
								sample/trick2015/ksk_2/entry.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								sample/trick2015/ksk_2/entry.rb
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | |||
| _='s %sSATISFIABLE';puts eval$<.read.gsub(/.*p.*?(\d+).*?$|\d+/m){$1?%w[?-* +'=-'=~/#{'(-?)'* }-*=(?=]*$1:$&>?0?"\\#$&$|":'$)(?='}+')/x?[_%p%i=0,[*$~].map{|x|x>?-?:v:eval(x+?1)*i-=1}*" "]:_%:UN' | ||||
							
								
								
									
										21
									
								
								sample/trick2015/ksk_2/quinn.cnf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								sample/trick2015/ksk_2/quinn.cnf
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| c  quinn.cnf | ||||
| c | ||||
| p cnf 16 18 | ||||
|   1    2  0 | ||||
|  -2   -4  0 | ||||
|   3    4  0 | ||||
|  -4   -5  0 | ||||
|   5   -6  0 | ||||
|   6   -7  0 | ||||
|   6    7  0 | ||||
|   7  -16  0 | ||||
|   8   -9  0 | ||||
|  -8  -14  0 | ||||
|   9   10  0 | ||||
|   9  -10  0 | ||||
| -10  -11  0 | ||||
|  10   12  0 | ||||
|  11   12  0 | ||||
|  13   14  0 | ||||
|  14  -15  0 | ||||
|  15   16  0 | ||||
							
								
								
									
										204
									
								
								sample/trick2015/ksk_2/remarks.markdown
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								sample/trick2015/ksk_2/remarks.markdown
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,204 @@ | |||
| ### Remarks | ||||
| 
 | ||||
| The program is run with a data file from the standard input, e.g., | ||||
| ```shell | ||||
|   ruby entry.rb < data | ||||
| ``` | ||||
| where ``<`` can be omitted.  The data file must be in the DIMACS CNF | ||||
| format (see Description for detail).  It has been confirmed to be run on | ||||
| ``` | ||||
|   ruby 1.9.3p385 (2013-02-06 revision 39114) [x86_64-darwin11.4.2] | ||||
|   ruby 2.0.0p481 (2014-05-08 revision 45883) [universal.x86_64-darwin13] | ||||
|   ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-linux] | ||||
| ``` | ||||
| For particular inputs, the program works differently on these environments | ||||
| (see Limitation). | ||||
| 
 | ||||
| 
 | ||||
| ### Description | ||||
| 
 | ||||
| The program is a very small SAT solver with 194 bytes making use of a | ||||
| powerful feature of Regexp matching in Ruby.  It receives a data file | ||||
| from the standard input in the DIMACS CNF that is a standard format | ||||
| for inputs of SAT solvers.  For example, the text in the DIMACS CNF | ||||
| format, | ||||
| ``` | ||||
| c | ||||
| c This is a sample input file. | ||||
| c | ||||
| p cnf 3 5 | ||||
|  1 -2  3 0 | ||||
| -1  2 0 | ||||
| -2 -3 0 | ||||
|  1  2 -3 0 | ||||
|  1  3 0 | ||||
| ``` | ||||
| corresponds to a propositional formula in conjunctive normal form, | ||||
| 
 | ||||
|   (L1      ∨ ¬L2 ∨ L3) ∧ | ||||
|   (¬L1 ∨      L2) ∧ | ||||
|   (¬L2 ∨ ¬L3) ∧ | ||||
|   (L1      ∨ L2      ∨ ¬L3) ∧ | ||||
|   (L1      ∨ L3). | ||||
| 
 | ||||
| In the DIMACS CNF format, the lines starting with ``c`` are comments | ||||
| that are allowed only before the line ``p cnf ...``.  The line ``p cnf | ||||
| 3 5`` represents that the problem is given in conjunctive normal form | ||||
| with 3 variables (L1,L2,and L3) and 5 clauses.  A clause is given by a | ||||
| sequence of the indices of positive literals and the negative indices | ||||
| of negative literals.  Each clause is terminated by ``0``.  For the | ||||
| input above, the program outputs | ||||
| ``` | ||||
| s SATISFIABLE | ||||
| v 1 2 -3 | ||||
| ``` | ||||
| because the formula is satisfiable by L1=true, L2=true, and L3=false. | ||||
| If an unsatisfiable formula is given, the program should output | ||||
| ``` | ||||
| s UNSATISFIABLE | ||||
| ``` | ||||
| This specification is common in most exiting SAT solvers and required | ||||
| for entries of [SAT competition](http://www.satcompetition.org/). | ||||
| 
 | ||||
| The program is very small with no other external libraries thanks to | ||||
| the wealth of string manipulations in Ruby.  It is much smaller than | ||||
| existing small SAT solvers like [minisat](http://minisat.se/) and | ||||
| [picosat](http://fmv.jku.at/picosat/)! | ||||
| 
 | ||||
| 
 | ||||
| ### Internals | ||||
| 
 | ||||
| The basic idea of the program is a translation from DIMACS CNF format | ||||
| into Ruby.  For example, the data file above is translated into a | ||||
| ``Regexp`` matching expression equivalent to | ||||
| ```ruby | ||||
|  '---=-' =~  | ||||
|  /(-?)(-?)(-?)-*=(?=\1$|-\2$|\3$|$)(?=-\1$|\2$|$)(?=-\2$|-\3$|$)(?=\1$|\2$|-\3$|$)(?=\1$|\3$|$)(?=)/ | ||||
| ``` | ||||
| that returns ``MatchData`` if the formula is satisfiable and otherwise | ||||
| returns ``nil``.  The beginning of regular expression | ||||
| ``(-?)(-?)(-?)-*=`` matches a string ``"---="`` so that each | ||||
| capturing pattern ``(-?)`` matches either ``"-"`` or `""`, which | ||||
| corresponds to an assignment of true or false, respectively, for a | ||||
| propositional variable.  Each clause is translated into positive | ||||
| lookahead assertion like ``(?=\1$|-\2$|\3$|$)`` that matches  | ||||
| ``"-"`` only when ``\1`` holds ``"-"``, ``\2`` holds ``""``, or ``\3`` | ||||
| holds ``"-"``.  This exactly corresponds to the condition for | ||||
| L1∨¬L2∨L3 to be true.  The last case ``|$`` never matches | ||||
| ``"-"`` but it is required for making the translation simple. | ||||
| The last meaningless positive lookahead assertion ``(?=)`` is added | ||||
| for a similar reason.  This translation is based on | ||||
| [Abigail's idea](http://perl.plover.com/NPC/NPC-3SAT.html) where a | ||||
| 3SAT formula is translated into a similar Perl regular expression. | ||||
| The differences are the submitted Ruby program translates directly | ||||
| from the DIMACS CNF format and tries to make the code shorter by using | ||||
| lookahead assertion which can also make matching more faster. | ||||
| 
 | ||||
| Thanks to the ``x`` option for regular expression, the input above is | ||||
| simply translated into | ||||
| ```ruby | ||||
|   ?-*3+'=-'=~/#{'(-?)'*3}-*=(?= | ||||
|    \1$| -\2$|  \3$| $)(?= | ||||
|   -\1$|  \2$| $)(?= | ||||
|   -\2$| -\3$| $)(?= | ||||
|    \1$|  \2$| -\3$| $)(?= | ||||
|    \1$|  \3$| $)(?= | ||||
|   )/x | ||||
| ``` | ||||
| which has a structure similar to the DIMACS CNF format. | ||||
| 
 | ||||
| The part of formatting outputs in the program is obfuscated as an | ||||
| inevitable result of 'golfing' the original program | ||||
| ```ruby | ||||
|    if ...the matching expression above... then | ||||
|      puts 's SATISFIABLE' | ||||
|      puts 'v '+$~[1..-1].map.with_index{|x,i| | ||||
|        if x == '-' then | ||||
|          i+1 | ||||
|        else | ||||
|          ['-',i+1].join | ||||
|        end | ||||
|      }.join(' ') | ||||
|    else | ||||
|      puts 's UNSATISFIABLE' | ||||
|    end | ||||
| ``` | ||||
| In the satisfiable case, the MatchData ``$~`` obtained by the regular expression | ||||
| has the form of | ||||
| ``` | ||||
|   #<MatchData "---=" 1:"-" 2:"-" 3:""> | ||||
| ``` | ||||
| which should be translated into a string ``1 2 -3``.  The golfed code simply | ||||
| does it by `eval(x+?1)*i-=1` where ``x`` is matched string ``"x"`` or ``""`` | ||||
| and ``i`` be a negated index. | ||||
| 
 | ||||
| 
 | ||||
| ### Data files | ||||
| 
 | ||||
| The submission includes some input files in the DIMACS CNF format for | ||||
| testing the program. | ||||
| 
 | ||||
| * [sample.cnf](sample.cnf) : an example shown above. | ||||
| 
 | ||||
| * [unsat.cnf](unsat.cnf) : an example of an unsatisfiable formula. | ||||
| 
 | ||||
| * [quinn.cnf](quinn.cnf) : an example from Quinn's text, 16 variables and 18 clauses | ||||
|    (available from [http://people.sc.fsu.edu/~jburkardt/data/cnf/cnf.html]) | ||||
| 
 | ||||
| * [abnormal.cnf](abnormal.cnf) : an example from [the unofficial manual of the DIMACS challenge](http://www.domagoj-babic.com/uploads/ResearchProjects/Spear/dimacs-cnf.pdf) | ||||
|   where a single clause may be on multiple lines. | ||||
| 
 | ||||
| * [uf20-01.cnf](uf20-01.cnf) : an example, with 20 variables and 91 clauses, from [SATLIB benchmark suite](http://www.cs.ubc.ca/~hoos/SATLIB/benchm.html).  The last two lines are removed from the original because they are illegal in the DIMACS CNF format (all examples in 'Uniform Random-3-SAT' of the linked page need this modification). | ||||
| 
 | ||||
| 
 | ||||
| ### Limitation | ||||
| 
 | ||||
| The program may not work when the number of variables exceeds 99 | ||||
| because ``\nnn`` in regular expression with number ``nnn`` does not | ||||
| always represent backreference but octal notation of characters.  For | ||||
| example, | ||||
| ```ruby | ||||
|   /#{"(x)"*999}:\502/=~"x"*999+":x" | ||||
|   /#{"(x)"*999}:\661/=~"x"*999+":x" | ||||
|   /#{"(x)"*999}:\775/=~"x"*999+":x" | ||||
| ``` | ||||
| fail due to the syntax error (invalid escape), while | ||||
| ```ruby | ||||
|   /#{"(x)"*999}:\508/=~"x"*999+":x" | ||||
|   /#{"(x)"*999}:\691/=~"x"*999+":x" | ||||
|   /#{"(x)"*999}:\785/=~"x"*999+":x" | ||||
| ``` | ||||
| succeed (to return 0) because 508, 691, and 785 are not in octal notation. | ||||
| Since Ruby 1.9.3 incorrectly returns ``nil`` instead of terminating | ||||
| with the error for | ||||
| ```ruby | ||||
|   /#{"(x)"*999}:\201/=~"x"*999+":x" | ||||
|   /#{"(x)"*999}:\325/=~"x"*999+":x" | ||||
| ``` | ||||
| the present SAT solver may unexpectedly return "UNSATISFIABLE" even | ||||
| for satisfiable inputs.  This happens when the number is in octal | ||||
| notation starting with either 2 or 3. | ||||
| 
 | ||||
| In the case of the number starting with 1, the code like the above | ||||
| does work on all versions of Ruby I tried.  For example, | ||||
| ```ruby | ||||
|   /#{"(x)"*999}:\101/=~"x"*999+":x" | ||||
|   /#{"(x)"*999}:\177/=~"x"*999+":x" | ||||
| ``` | ||||
| succeed (to return 0).  Interestingly,  | ||||
| ```ruby | ||||
|   /#{"(x)"*999}:\101/=~"x"*999+":\101" | ||||
|   /#{"(x)"*999}:\177/=~"x"*999+":\177" | ||||
| ``` | ||||
| return ``nil``, while | ||||
| ```ruby | ||||
|   /:\101/=~":\101" | ||||
|   /:\177/=~":\177" | ||||
| ``` | ||||
| succeed to return 0.  The meaning of ``\1nn`` in regular expression | ||||
| seems to depend on the existence of capturing expressions. | ||||
| 
 | ||||
| In spite of these Ruby's behaviors, we have a good news!  The present | ||||
| SAT sover does not suffer from the issues because the program cannot | ||||
| return solutions in practical time for inputs with variables more than | ||||
| 40. | ||||
							
								
								
									
										9
									
								
								sample/trick2015/ksk_2/sample.cnf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								sample/trick2015/ksk_2/sample.cnf
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| c | ||||
| c This is a sample input file. | ||||
| c | ||||
| p cnf 3 5 | ||||
|  1 -2  3 0 | ||||
| -1  2 0 | ||||
| -2 -3 0 | ||||
|  1  2 -3 0 | ||||
|  1  3 0 | ||||
							
								
								
									
										99
									
								
								sample/trick2015/ksk_2/uf20-01.cnf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								sample/trick2015/ksk_2/uf20-01.cnf
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,99 @@ | |||
| c This Formular is generated by mcnf | ||||
| c | ||||
| c    horn? no  | ||||
| c    forced? no  | ||||
| c    mixed sat? no  | ||||
| c    clause length = 3  | ||||
| c | ||||
| p cnf 20  91  | ||||
|  4 -18 19 0 | ||||
| 3 18 -5 0 | ||||
| -5 -8 -15 0 | ||||
| -20 7 -16 0 | ||||
| 10 -13 -7 0 | ||||
| -12 -9 17 0 | ||||
| 17 19 5 0 | ||||
| -16 9 15 0 | ||||
| 11 -5 -14 0 | ||||
| 18 -10 13 0 | ||||
| -3 11 12 0 | ||||
| -6 -17 -8 0 | ||||
| -18 14 1 0 | ||||
| -19 -15 10 0 | ||||
| 12 18 -19 0 | ||||
| -8 4 7 0 | ||||
| -8 -9 4 0 | ||||
| 7 17 -15 0 | ||||
| 12 -7 -14 0 | ||||
| -10 -11 8 0 | ||||
| 2 -15 -11 0 | ||||
| 9 6 1 0 | ||||
| -11 20 -17 0 | ||||
| 9 -15 13 0 | ||||
| 12 -7 -17 0 | ||||
| -18 -2 20 0 | ||||
| 20 12 4 0 | ||||
| 19 11 14 0 | ||||
| -16 18 -4 0 | ||||
| -1 -17 -19 0 | ||||
| -13 15 10 0 | ||||
| -12 -14 -13 0 | ||||
| 12 -14 -7 0 | ||||
| -7 16 10 0 | ||||
| 6 10 7 0 | ||||
| 20 14 -16 0 | ||||
| -19 17 11 0 | ||||
| -7 1 -20 0 | ||||
| -5 12 15 0 | ||||
| -4 -9 -13 0 | ||||
| 12 -11 -7 0 | ||||
| -5 19 -8 0 | ||||
| 1 16 17 0 | ||||
| 20 -14 -15 0 | ||||
| 13 -4 10 0 | ||||
| 14 7 10 0 | ||||
| -5 9 20 0 | ||||
| 10 1 -19 0 | ||||
| -16 -15 -1 0 | ||||
| 16 3 -11 0 | ||||
| -15 -10 4 0 | ||||
| 4 -15 -3 0 | ||||
| -10 -16 11 0 | ||||
| -8 12 -5 0 | ||||
| 14 -6 12 0 | ||||
| 1 6 11 0 | ||||
| -13 -5 -1 0 | ||||
| -7 -2 12 0 | ||||
| 1 -20 19 0 | ||||
| -2 -13 -8 0 | ||||
| 15 18 4 0 | ||||
| -11 14 9 0 | ||||
| -6 -15 -2 0 | ||||
| 5 -12 -15 0 | ||||
| -6 17 5 0 | ||||
| -13 5 -19 0 | ||||
| 20 -1 14 0 | ||||
| 9 -17 15 0 | ||||
| -5 19 -18 0 | ||||
| -12 8 -10 0 | ||||
| -18 14 -4 0 | ||||
| 15 -9 13 0 | ||||
| 9 -5 -1 0 | ||||
| 10 -19 -14 0 | ||||
| 20 9 4 0 | ||||
| -9 -2 19 0 | ||||
| -5 13 -17 0 | ||||
| 2 -10 -18 0 | ||||
| -18 3 11 0 | ||||
| 7 -9 17 0 | ||||
| -15 -6 -3 0 | ||||
| -2 3 -13 0 | ||||
| 12 3 -2 0 | ||||
| -2 -3 17 0 | ||||
| 20 -15 -16 0 | ||||
| -5 -17 -19 0 | ||||
| -20 -18 11 0 | ||||
| -9 1 -5 0 | ||||
| -19 9 17 0 | ||||
| 12 -2 17 0 | ||||
| 4 -16 -5 0 | ||||
							
								
								
									
										11
									
								
								sample/trick2015/ksk_2/unsat.cnf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								sample/trick2015/ksk_2/unsat.cnf
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| c | ||||
| c This is a sample input file. | ||||
| c (unsatisfiable) | ||||
| c | ||||
| p cnf 3 5 | ||||
| 1 -2 3 0 | ||||
| -1 2 0 | ||||
| -2 -3 0 | ||||
| 1 2 -3 0 | ||||
| 1 3 0 | ||||
| -1 -2 3 0 | ||||
							
								
								
									
										1
									
								
								sample/trick2015/monae/authors.markdown
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								sample/trick2015/monae/authors.markdown
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | |||
| monae (@monae, jp) | ||||
							
								
								
									
										26
									
								
								sample/trick2015/monae/entry.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								sample/trick2015/monae/entry.rb
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | |||
|           ;;  ;;          ;;  ;; | ||||
|            ;;  ;;          ;;  ;; | ||||
|         ;;eval$s        =%q[i=1# | ||||
|          eval(%q[        xxxxxxxx | ||||
|         xx  xxxx  xx    xx  xxxx  xx | ||||
|          xx  xxxx  xx    xx  xxxx  xx | ||||
|             xxxxxxxx        xxxxxxxx | ||||
|              xxxxxxxx        xxxxxxxx | ||||
|   xx  xx  xxxxxxxxxx  xx  xxxxxxxx | ||||
|    j,  t,  p=0,[?;],"  ev  al$s=%qx | ||||
| [#$s]".split*"";i,j,t=i-j,i+j,(x | ||||
|  [b=?\s]*j.abs+t).map{|s|r=t.shix | ||||
| ft  ||b;r.gsub!(?;){p.slice!0}if  $x | ||||
|  f|  |=p>p=p.center(i*i+j*j,?;);r  ,x | ||||
|     s=[s,r]if(i*j<0);(b*i.abs+s).ljx | ||||
|      ust(r.size).gsub(b){r[$`.size]|x | ||||
|   |b}}unti  l$  f;puts(t)#  xx  xx | ||||
|    xxxxxxxx  xx  xxxxxxxxxx  xx  xx | ||||
| xxxxxxxx        xxxxxxxx | ||||
|  xxxxxxxx        xxxxxxxx | ||||
| xx  xxxx  xx    xx  xxxx  xx | ||||
|  xx  xxxx  xx    xx  xxxx  xx | ||||
|     xxxxxxxx        x].gsub\ | ||||
|      /x.*|\s/        ,"")#];; | ||||
|     ;;  ;;          ;;  ;; | ||||
|      ;;  ;;          ;;  ;; | ||||
							
								
								
									
										25
									
								
								sample/trick2015/monae/remarks.markdown
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								sample/trick2015/monae/remarks.markdown
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | |||
| 
 | ||||
| # How to run | ||||
| 
 | ||||
| ``` | ||||
| ruby entry.rb | ||||
| ruby entry.rb | ruby | ||||
| ruby entry.rb | ruby | ruby | ||||
| ruby entry.rb | ruby | ruby | ruby | ||||
| ... | ||||
| ``` | ||||
| 
 | ||||
| Confirmed on the following environments: | ||||
| - ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-darwin14] | ||||
| - ruby 2.0.0p353 (2013-11-22) [i386-mingw32] | ||||
| 
 | ||||
| # Description | ||||
| 
 | ||||
| A simple quine which prints itself twice | ||||
| on a slightly complex base. | ||||
| 
 | ||||
| > geminum caput amphisbaenae, hoc est et a cauda, | ||||
| > tamquam parum esset uno ore fundi venenum. | ||||
| > aliis squamas esse, aliis picturas, omnibus exitiale virus. | ||||
| > | ||||
| > — <cite>GAIUS PLINIUS SECUNDUS, Naturalis Historia 8.85.1</cite> | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 mame
						mame