Use C extension to remove ImageMagick dependency (#40)

This commit is contained in:
Jason Lee 2017-01-22 10:16:57 +08:00 committed by GitHub
parent 9159b8cd10
commit 93ac160201
26 changed files with 682 additions and 334 deletions

4
.gitignore vendored
View File

@ -9,6 +9,10 @@
/test/version_tmp/ /test/version_tmp/
/tmp/ /tmp/
.DS_Store .DS_Store
*.bundle
*.o
ext/rucaptcha/Makefile
ext/rucaptcha/*.gif
## RubyMine Ide files ## RubyMine Ide files
.idea/ .idea/

View File

@ -3,8 +3,5 @@ rvm:
- 2.2.3 - 2.2.3
services: services:
- memcached - memcached
addons: script:
apt: - bundle exec rake compile && bundle exec rake
packages:
- imagemagick
- tesseract-ocr

View File

@ -1,3 +1,15 @@
2.0.0
-----
*Break Changes!*
WARNING!: This version have so many break changes!
- Use C ext instead of ImageMagick, now it's no dependencies!
- New captcha style.
- Remove `len`, `font_size`, `cache_limit` config key, no support now.
- Output `GIF` format.
1.2.0 1.2.0
----- -----

View File

@ -1,7 +1,7 @@
PATH PATH
remote: . remote: .
specs: specs:
rucaptcha (1.2.0) rucaptcha (2.0.0)
railties (>= 3.2) railties (>= 3.2)
GEM GEM
@ -96,6 +96,8 @@ GEM
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
rake (11.3.0) rake (11.3.0)
rake-compiler (1.0.3)
rake
rspec (3.3.0) rspec (3.3.0)
rspec-core (~> 3.3.0) rspec-core (~> 3.3.0)
rspec-expectations (~> 3.3.0) rspec-expectations (~> 3.3.0)
@ -135,8 +137,9 @@ DEPENDENCIES
dalli dalli
rails rails
rake rake
rake-compiler (~> 1)
rspec rspec
rucaptcha! rucaptcha!
BUNDLED WITH BUNDLED WITH
1.13.6 1.13.7

View File

@ -4,44 +4,20 @@
[![Build Status](https://travis-ci.org/huacnlee/rucaptcha.svg)](https://travis-ci.org/huacnlee/rucaptcha) [![Build Status](https://travis-ci.org/huacnlee/rucaptcha.svg)](https://travis-ci.org/huacnlee/rucaptcha)
[![Code Climate](https://codeclimate.com/github/huacnlee/rucaptcha/badges/gpa.svg)](https://codeclimate.com/github/huacnlee/rucaptcha) [![Code Climate](https://codeclimate.com/github/huacnlee/rucaptcha/badges/gpa.svg)](https://codeclimate.com/github/huacnlee/rucaptcha)
This is a Captcha gem for Rails Applications. It runs an ImageMagick command to draw Captcha image - so it has NO performance issues or memory leak issues. **There is NO: RMagick** This is a Captcha gem for Rails Applications. It drawing captcha image with C code.
## Example ## Example
![1](https://cloud.githubusercontent.com/assets/5518/15423974/b186b0d6-1eb2-11e6-9c0e-4cc3a66f32c8.png) <img src="https://cloud.githubusercontent.com/assets/5518/22151425/e02390c8-df58-11e6-974d-5eb9b1a4e577.gif" width="150px" /> <img src="https://cloud.githubusercontent.com/assets/5518/22151427/e4939d92-df58-11e6-9754-4a46a86acea8.gif" width="150px" /> <img src="https://cloud.githubusercontent.com/assets/5518/22151431/e494576e-df58-11e6-9845-a5590904c175.gif" width="150px" /> <img src="https://cloud.githubusercontent.com/assets/5518/22151432/e495066e-df58-11e6-92b8-38b40b73aba0.gif" width="150px" /> <img src="https://cloud.githubusercontent.com/assets/5518/22151428/e49404ee-df58-11e6-8e2d-8b17b33a3710.gif" width="150px" /> <img src="https://cloud.githubusercontent.com/assets/5518/22151430/e4942406-df58-11e6-9ff8-6e2325304b41.gif" width="150px" /> <img src="https://cloud.githubusercontent.com/assets/5518/22151429/e4941ae2-df58-11e6-8107-757296573b2f.gif" width="150px" /> <img src="https://cloud.githubusercontent.com/assets/5518/22151433/e4c7c89c-df58-11e6-9853-1ffbb4986962.gif" width="150px" /> <img src="https://cloud.githubusercontent.com/assets/5518/22151435/e4c97ea8-df58-11e6-8959-b4c78716271d.gif" width="150px" /> <img src="https://cloud.githubusercontent.com/assets/5518/22151436/e4cc09f2-df58-11e6-965c-673333b33c0d.gif" width="150px" /> <img src="https://cloud.githubusercontent.com/assets/5518/22151434/e4c87788-df58-11e6-9490-c255aaafce71.gif" width="150px" /> <img src="https://cloud.githubusercontent.com/assets/5518/22151445/ee35ff66-df58-11e6-8660-a3673ef3f5ee.gif" width="150px" /> <img src="https://cloud.githubusercontent.com/assets/5518/22151446/ee67b074-df58-11e6-9b95-7d53eec21c33.gif" width="150px" />
![2](https://cloud.githubusercontent.com/assets/5518/15423975/b1887b6e-1eb2-11e6-895f-5629f82697d3.png)
![3](https://cloud.githubusercontent.com/assets/5518/15423978/b18f08ee-1eb2-11e6-9670-c21dba290e04.png)
![4](https://cloud.githubusercontent.com/assets/5518/15423976/b18b6946-1eb2-11e6-8413-700ded157262.png)
![5](https://cloud.githubusercontent.com/assets/5518/15423977/b18e7c62-1eb2-11e6-96f7-5bd6981d4185.png)
![6](https://cloud.githubusercontent.com/assets/5518/15423979/b19175d4-1eb2-11e6-9417-7d496fb996b4.png)
![7](https://cloud.githubusercontent.com/assets/5518/15423980/b1caf944-1eb2-11e6-862e-78c0a9360b43.png)
Idea by: https://ruby-china.org/topics/20558#reply4
[中文介绍和使用说明](https://ruby-china.org/topics/27832) [中文介绍和使用说明](https://ruby-china.org/topics/27832)
## Feature ## Feature
- Only need `ImageMagick`, No `RMagick`, No `mini_magick`; - No dependencies. No ImageMagick, No RMagick.
- For Rails Application; - For Rails Application;
- Simple, Easy to use; - Simple, Easy to use;
- File Caching for performance. - High performance.
## Requirements
- ImageMagick 6.9+
#### Ubuntu
```
sudo apt-get install imagemagick ghostscript
```
#### Mac OS X
```bash
brew install imagemagick ghostscript
```
## Usage ## Usage
@ -55,17 +31,10 @@ Create `config/initializers/rucaptcha.rb`
```rb ```rb
RuCaptcha.configure do RuCaptcha.configure do
# Number of chars, default: 4
self.len = 4
# Image font size, default: 45
self.font_size = 45
# Cache generated images in file store, this is config files limit, default: 100
# set 0 to disable file cache.
self.cache_limit = 100
# Custom captcha code expire time if you need, default: 2 minutes
# self.expires_in = 120
# Color style, default: :colorful, allows: [:colorful, :black_white] # Color style, default: :colorful, allows: [:colorful, :black_white]
# self.style = :colorful # self.style = :colorful
# Custom captcha code expire time if you need, default: 2 minutes
# self.expires_in = 120
# [Requirement] # [Requirement]
# Store Captcha code where, this config more like Rails config.cache_store # Store Captcha code where, this config more like Rails config.cache_store
# default: Rails application config.cache_store # default: Rails application config.cache_store
@ -99,7 +68,7 @@ class AccountController < ApplicationController
end end
``` ```
> TIP: Sometime you may need keep last verified captcha code in session on `verify_rucaptcha?` method call, you can use `keep_session: true`. For example: ` Verify_rucaptcha? (@user, keep_session: true) `. > TIP: Sometime you may need keep last verified captcha code in session on `verify_rucaptcha?` method call, you can use `keep_session: true`. For example: `verify_rucaptcha? @user, keep_session: true`.
View `app/views/account/new.html.erb` View `app/views/account/new.html.erb`

View File

@ -1,5 +1,18 @@
require "bundler/gem_tasks" require "bundler/gem_tasks"
require "rspec/core/rake_task" require "rspec/core/rake_task"
require 'rake/extensiontask'
Rake::ExtensionTask.new "rucaptcha" do |ext|
ext.lib_dir = "lib/rucaptcha"
end
RSpec::Core::RakeTask.new(:spec) RSpec::Core::RakeTask.new(:spec)
task default: :spec task default: :spec
task :preview do
require 'rucaptcha'
res = RuCaptcha.create(1)
puts res[1]
end

View File

@ -3,11 +3,13 @@ module RuCaptcha
def index def index
headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate' headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
headers['Pragma'] = 'no-cache' headers['Pragma'] = 'no-cache'
data = generate_rucaptcha
opts = { disposition: 'inline', type: 'image/gif' }
if Gem.win_platform? if Gem.win_platform?
send_file generate_rucaptcha, disposition: 'inline', type: 'image/png' send_file data, opts
else else
send_data generate_rucaptcha, disposition: 'inline', type: 'image/png' send_data data, opts
end end
end end
end end

345
ext/rucaptcha/colors.h Normal file
View File

@ -0,0 +1,345 @@
// Colors from
// https://material.io/guidelines/style/color.html#color-ui-color-palette
static char *colors[] = {
// Black 500 #000000
"GIF89a" "\xc8\0\x46\0" "\x83" "\0\0"
"\x11\x11\x11"
"\x11\x11\x11"
"\x11\x11\x11"
"\x11\x11\x11"
"\x11\x11\x11"
"\x11\x11\x11"
"\x11\x11\x11"
"\x11\x11\x11"
"\x11\x11\x11"
"\x11\x11\x11"
"\x11\x11\x11"
"\x11\x11\x11"
"\x11\x11\x11"
"\x11\x11\x11"
"\x11\x11\x11"
"\xff\xff\xff" "," "\0\0\0\0" "\xc8\0\x46\0" "\0" "\x04",
// Red 500 #F44336
"GIF89a" "\xc8\0\x46\0" "\x83" "\0\0"
"\xF4\x43\x36"
"\xF4\x43\x36"
"\xF4\x43\x36"
"\xF4\x43\x36"
"\xF4\x43\x36"
"\xF4\x43\x36"
"\xF4\x43\x36"
"\xF4\x43\x36"
"\xF4\x43\x36"
"\xF4\x43\x36"
"\xF4\x43\x36"
"\xF4\x43\x36"
"\xF4\x43\x36"
"\xF4\x43\x36"
"\xF4\x43\x36"
"\xff\xff\xff" "," "\0\0\0\0" "\xc8\0\x46\0" "\0" "\x04",
// Pink 500 #E91E63
"GIF89a" "\xc8\0\x46\0" "\x83" "\0\0"
"\xE9\x1E\x63"
"\xE9\x1E\x63"
"\xE9\x1E\x63"
"\xE9\x1E\x63"
"\xE9\x1E\x63"
"\xE9\x1E\x63"
"\xE9\x1E\x63"
"\xE9\x1E\x63"
"\xE9\x1E\x63"
"\xE9\x1E\x63"
"\xE9\x1E\x63"
"\xE9\x1E\x63"
"\xE9\x1E\x63"
"\xE9\x1E\x63"
"\xE9\x1E\x63"
"\xff\xff\xff" "," "\0\0\0\0" "\xc8\0\x46\0" "\0" "\x04",
// Purple 500 #9C27B0
"GIF89a" "\xc8\0\x46\0" "\x83" "\0\0"
"\x9C\x27\xB0"
"\x9C\x27\xB0"
"\x9C\x27\xB0"
"\x9C\x27\xB0"
"\x9C\x27\xB0"
"\x9C\x27\xB0"
"\x9C\x27\xB0"
"\x9C\x27\xB0"
"\x9C\x27\xB0"
"\x9C\x27\xB0"
"\x9C\x27\xB0"
"\x9C\x27\xB0"
"\x9C\x27\xB0"
"\x9C\x27\xB0"
"\x9C\x27\xB0"
"\xff\xff\xff" "," "\0\0\0\0" "\xc8\0\x46\0" "\0" "\x04",
// Deep Purple 500 #673AB7
"GIF89a" "\xc8\0\x46\0" "\x83" "\0\0"
"\x67\x3A\xB7"
"\x67\x3A\xB7"
"\x67\x3A\xB7"
"\x67\x3A\xB7"
"\x67\x3A\xB7"
"\x67\x3A\xB7"
"\x67\x3A\xB7"
"\x67\x3A\xB7"
"\x67\x3A\xB7"
"\x67\x3A\xB7"
"\x67\x3A\xB7"
"\x67\x3A\xB7"
"\x67\x3A\xB7"
"\x67\x3A\xB7"
"\x67\x3A\xB7"
"\xff\xff\xff" "," "\0\0\0\0" "\xc8\0\x46\0" "\0" "\x04",
// Indigo 500 #3F51B5
"GIF89a" "\xc8\0\x46\0" "\x83" "\0\0"
"\x3F\x51\xB5"
"\x3F\x51\xB5"
"\x3F\x51\xB5"
"\x3F\x51\xB5"
"\x3F\x51\xB5"
"\x3F\x51\xB5"
"\x3F\x51\xB5"
"\x3F\x51\xB5"
"\x3F\x51\xB5"
"\x3F\x51\xB5"
"\x3F\x51\xB5"
"\x3F\x51\xB5"
"\x3F\x51\xB5"
"\x3F\x51\xB5"
"\x3F\x51\xB5"
"\xff\xff\xff" "," "\0\0\0\0" "\xc8\0\x46\0" "\0" "\x04",
// Blue 500 #2196F3
"GIF89a" "\xc8\0\x46\0" "\x83" "\0\0"
"\x21\x96\xF3"
"\x21\x96\xF3"
"\x21\x96\xF3"
"\x21\x96\xF3"
"\x21\x96\xF3"
"\x21\x96\xF3"
"\x21\x96\xF3"
"\x21\x96\xF3"
"\x21\x96\xF3"
"\x21\x96\xF3"
"\x21\x96\xF3"
"\x21\x96\xF3"
"\x21\x96\xF3"
"\x21\x96\xF3"
"\x21\x96\xF3"
"\xff\xff\xff" "," "\0\0\0\0" "\xc8\0\x46\0" "\0" "\x04",
// Light Blue 500 #03A9F4
"GIF89a" "\xc8\0\x46\0" "\x83" "\0\0"
"\x03\xA9\xF4"
"\x03\xA9\xF4"
"\x03\xA9\xF4"
"\x03\xA9\xF4"
"\x03\xA9\xF4"
"\x03\xA9\xF4"
"\x03\xA9\xF4"
"\x03\xA9\xF4"
"\x03\xA9\xF4"
"\x03\xA9\xF4"
"\x03\xA9\xF4"
"\x03\xA9\xF4"
"\x03\xA9\xF4"
"\x03\xA9\xF4"
"\x03\xA9\xF4"
"\xff\xff\xff" "," "\0\0\0\0" "\xc8\0\x46\0" "\0" "\x04",
// Cyan 500 #00BCD4
"GIF89a" "\xc8\0\x46\0" "\x83" "\0\0"
"\x00\xBC\xD4"
"\x00\xBC\xD4"
"\x00\xBC\xD4"
"\x00\xBC\xD4"
"\x00\xBC\xD4"
"\x00\xBC\xD4"
"\x00\xBC\xD4"
"\x00\xBC\xD4"
"\x00\xBC\xD4"
"\x00\xBC\xD4"
"\x00\xBC\xD4"
"\x00\xBC\xD4"
"\x00\xBC\xD4"
"\x00\xBC\xD4"
"\x00\xBC\xD4"
"\xff\xff\xff" "," "\0\0\0\0" "\xc8\0\x46\0" "\0" "\x04",
// Teal 500 #009688
"GIF89a" "\xc8\0\x46\0" "\x83" "\0\0"
"\x00\x96\x88"
"\x00\x96\x88"
"\x00\x96\x88"
"\x00\x96\x88"
"\x00\x96\x88"
"\x00\x96\x88"
"\x00\x96\x88"
"\x00\x96\x88"
"\x00\x96\x88"
"\x00\x96\x88"
"\x00\x96\x88"
"\x00\x96\x88"
"\x00\x96\x88"
"\x00\x96\x88"
"\x00\x96\x88"
"\xff\xff\xff" "," "\0\0\0\0" "\xc8\0\x46\0" "\0" "\x04",
// Green 500 #4CAF50
"GIF89a" "\xc8\0\x46\0" "\x83" "\0\0"
"\x4C\xAF\x50"
"\x4C\xAF\x50"
"\x4C\xAF\x50"
"\x4C\xAF\x50"
"\x4C\xAF\x50"
"\x4C\xAF\x50"
"\x4C\xAF\x50"
"\x4C\xAF\x50"
"\x4C\xAF\x50"
"\x4C\xAF\x50"
"\x4C\xAF\x50"
"\x4C\xAF\x50"
"\x4C\xAF\x50"
"\x4C\xAF\x50"
"\x4C\xAF\x50"
"\xff\xff\xff" "," "\0\0\0\0" "\xc8\0\x46\0" "\0" "\x04",
// Light Green 500 #8BC34A
"GIF89a" "\xc8\0\x46\0" "\x83" "\0\0"
"\x8B\xC3\x4A"
"\x8B\xC3\x4A"
"\x8B\xC3\x4A"
"\x8B\xC3\x4A"
"\x8B\xC3\x4A"
"\x8B\xC3\x4A"
"\x8B\xC3\x4A"
"\x8B\xC3\x4A"
"\x8B\xC3\x4A"
"\x8B\xC3\x4A"
"\x8B\xC3\x4A"
"\x8B\xC3\x4A"
"\x8B\xC3\x4A"
"\x8B\xC3\x4A"
"\x8B\xC3\x4A"
"\xff\xff\xff" "," "\0\0\0\0" "\xc8\0\x46\0" "\0" "\x04",
// Lime 500 #CDDC39
"GIF89a" "\xc8\0\x46\0" "\x83" "\0\0"
"\xCD\xDC\x39"
"\xCD\xDC\x39"
"\xCD\xDC\x39"
"\xCD\xDC\x39"
"\xCD\xDC\x39"
"\xCD\xDC\x39"
"\xCD\xDC\x39"
"\xCD\xDC\x39"
"\xCD\xDC\x39"
"\xCD\xDC\x39"
"\xCD\xDC\x39"
"\xCD\xDC\x39"
"\xCD\xDC\x39"
"\xCD\xDC\x39"
"\xCD\xDC\x39"
"\xff\xff\xff" "," "\0\0\0\0" "\xc8\0\x46\0" "\0" "\x04",
// Yellow 500 #FFEB3B
"GIF89a" "\xc8\0\x46\0" "\x83" "\0\0"
"\xFF\xEB\x3B"
"\xFF\xEB\x3B"
"\xFF\xEB\x3B"
"\xFF\xEB\x3B"
"\xFF\xEB\x3B"
"\xFF\xEB\x3B"
"\xFF\xEB\x3B"
"\xFF\xEB\x3B"
"\xFF\xEB\x3B"
"\xFF\xEB\x3B"
"\xFF\xEB\x3B"
"\xFF\xEB\x3B"
"\xFF\xEB\x3B"
"\xFF\xEB\x3B"
"\xFF\xEB\x3B"
"\xff\xff\xff" "," "\0\0\0\0" "\xc8\0\x46\0" "\0" "\x04",
// Amber 500 #FFC107
"GIF89a" "\xc8\0\x46\0" "\x83" "\0\0"
"\xFF\xC1\x07"
"\xFF\xC1\x07"
"\xFF\xC1\x07"
"\xFF\xC1\x07"
"\xFF\xC1\x07"
"\xFF\xC1\x07"
"\xFF\xC1\x07"
"\xFF\xC1\x07"
"\xFF\xC1\x07"
"\xFF\xC1\x07"
"\xFF\xC1\x07"
"\xFF\xC1\x07"
"\xFF\xC1\x07"
"\xFF\xC1\x07"
"\xFF\xC1\x07"
"\xff\xff\xff" "," "\0\0\0\0" "\xc8\0\x46\0" "\0" "\x04",
// Orange 500 #FF9800
"GIF89a" "\xc8\0\x46\0" "\x83" "\0\0"
"\xFF\x98\x00"
"\xFF\x98\x00"
"\xFF\x98\x00"
"\xFF\x98\x00"
"\xFF\x98\x00"
"\xFF\x98\x00"
"\xFF\x98\x00"
"\xFF\x98\x00"
"\xFF\x98\x00"
"\xFF\x98\x00"
"\xFF\x98\x00"
"\xFF\x98\x00"
"\xFF\x98\x00"
"\xFF\x98\x00"
"\xFF\x98\x00"
"\xff\xff\xff" "," "\0\0\0\0" "\xc8\0\x46\0" "\0" "\x04",
// Deep Orange 500 #FF5722
"GIF89a" "\xc8\0\x46\0" "\x83" "\0\0"
"\xFF\x57\x22"
"\xFF\x57\x22"
"\xFF\x57\x22"
"\xFF\x57\x22"
"\xFF\x57\x22"
"\xFF\x57\x22"
"\xFF\x57\x22"
"\xFF\x57\x22"
"\xFF\x57\x22"
"\xFF\x57\x22"
"\xFF\x57\x22"
"\xFF\x57\x22"
"\xFF\x57\x22"
"\xFF\x57\x22"
"\xFF\x57\x22"
"\xff\xff\xff" "," "\0\0\0\0" "\xc8\0\x46\0" "\0" "\x04",
// Brown 500 #795548
"GIF89a" "\xc8\0\x46\0" "\x83" "\0\0"
"\x79\x55\x48"
"\x79\x55\x48"
"\x79\x55\x48"
"\x79\x55\x48"
"\x79\x55\x48"
"\x79\x55\x48"
"\x79\x55\x48"
"\x79\x55\x48"
"\x79\x55\x48"
"\x79\x55\x48"
"\x79\x55\x48"
"\x79\x55\x48"
"\x79\x55\x48"
"\x79\x55\x48"
"\x79\x55\x48"
"\xff\xff\xff" "," "\0\0\0\0" "\xc8\0\x46\0" "\0" "\x04"
};

2
ext/rucaptcha/extconf.rb Normal file
View File

@ -0,0 +1,2 @@
require 'mkmf'
create_makefile('rucaptcha/rucaptcha')

27
ext/rucaptcha/font.h Normal file
View File

@ -0,0 +1,27 @@
static int8_t lt0[]={-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-4,11,7,5,3,1,0,0,0,1,3,7,13,-100,-2,11,3,0,0,0,0,0,0,0,0,0,0,0,0,9,-100,-1,7,0,0,0,0,0,0,3,9,11,9,3,0,0,0,0,13,-100,9,0,0,0,0,0,0,3,-5,3,0,0,0,7,-100,5,0,0,0,0,0,1,13,-5,9,0,0,0,1,-100,7,0,0,0,0,1,13,-6,13,0,0,0,0,-100,-1,9,1,0,5,13,-8,0,0,0,0,13,-100,-14,0,0,0,0,11,-100,-14,0,0,0,0,11,-100,-14,0,0,0,0,11,-100,-12,13,5,0,0,0,0,11,-100,-8,13,9,5,1,0,0,0,0,0,0,11,-100,-4,13,7,3,1,0,0,0,0,1,1,0,0,0,0,11,-100,-2,13,5,0,0,0,0,0,5,9,13,-2,0,0,0,0,11,-100,-1,13,1,0,0,0,0,7,-6,0,0,0,0,11,-100,13,1,0,0,0,0,13,-7,0,0,0,0,11,-100,5,0,0,0,0,5,-8,0,0,0,0,11,-100,0,0,0,0,0,11,-8,0,0,0,0,11,-100,0,0,0,0,0,13,-7,13,0,0,0,0,11,-100,1,0,0,0,0,-7,9,0,0,0,0,0,9,-3,9,-100,5,0,0,0,0,3,13,-3,11,3,0,0,0,0,0,0,0,9,13,3,5,-100,13,0,0,0,0,0,0,1,1,0,0,0,1,11,9,0,0,0,0,0,0,1,13,-100,-1,11,1,0,0,0,0,0,0,0,0,5,-3,9,0,0,0,0,0,11,-100,-2,13,7,3,0,0,0,3,7,13,-5,9,0,1,3,9,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-101};
static int8_t lt1[]={-100,-100,-4,13,5,0,3,-100,-3,11,1,0,0,0,7,-100,-2,7,0,0,0,0,0,3,-100,13,3,0,0,0,0,0,0,5,-100,1,0,0,0,0,0,0,0,9,-100,1,0,0,0,0,0,0,0,13,-100,13,3,0,0,0,0,0,1,-100,-2,5,0,0,0,0,5,-100,-3,0,0,0,0,9,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,9,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,3,13,9,5,3,1,0,0,1,3,5,9,-100,-3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,13,-100,-3,0,0,0,0,0,5,13,-2,13,11,9,5,0,0,0,0,1,13,-100,-3,0,0,0,0,1,-8,13,1,0,0,0,5,-100,-3,3,0,0,0,5,-9,13,0,0,0,0,11,-100,-3,0,0,0,0,9,-10,7,0,0,0,5,-100,-3,1,0,0,0,11,-10,13,0,0,0,0,-100,-3,3,0,0,0,11,-11,3,0,0,0,11,-100,-3,1,0,0,0,11,-11,7,0,0,0,7,-100,-3,0,0,0,0,11,-11,9,0,0,0,3,-100,-3,0,0,0,0,11,-11,11,0,0,0,1,-100,-3,0,0,0,0,11,-11,11,0,0,0,1,-100,-3,0,0,0,0,11,-11,11,0,0,0,0,-100,-3,0,0,0,0,11,-11,9,0,0,0,0,-100,-3,0,0,0,0,11,-11,7,0,0,0,3,-100,-3,0,0,0,0,11,-11,3,0,0,0,7,-100,-3,0,0,0,0,11,-11,0,0,0,0,11,-100,-3,0,0,0,0,11,-10,9,0,0,0,3,-100,-3,0,0,0,0,9,-10,3,0,0,0,11,-100,-3,0,0,0,0,3,-9,11,0,0,0,5,-100,-2,13,0,0,0,0,0,9,-7,11,1,0,0,3,-100,-2,7,0,0,0,0,0,0,7,13,-2,13,9,3,0,0,0,3,13,-100,-2,13,0,0,5,13,11,1,0,0,0,0,0,0,0,0,0,7,-100,-3,9,11,-4,7,3,1,0,0,1,5,9,13,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-101};
static int8_t lt2[]={-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-6,13,7,3,1,0,0,1,3,7,13,-100,-5,9,0,0,0,0,0,0,0,0,0,0,5,13,-100,-3,13,3,0,0,0,5,13,13,7,0,0,0,0,0,1,13,-100,-2,13,1,0,0,0,9,-4,7,0,0,0,0,0,1,-100,-1,13,1,0,0,0,9,-6,5,0,0,0,0,0,13,-100,-1,5,0,0,0,3,-8,1,0,0,0,3,-100,13,0,0,0,0,11,-8,13,3,0,3,-100,7,0,0,0,1,-100,5,0,0,0,5,-100,3,0,0,0,9,-100,1,0,0,0,11,-100,0,0,0,0,11,-100,0,0,0,0,11,-100,0,0,0,0,11,-100,0,0,0,0,9,-100,1,0,0,0,5,-100,5,0,0,0,0,13,-100,11,0,0,0,0,7,-100,-1,3,0,0,0,0,13,-100,-1,11,0,0,0,0,3,-12,9,-100,-2,7,0,0,0,0,3,13,-8,9,1,3,-100,-3,5,0,0,0,0,1,9,-5,9,3,0,0,11,-100,-4,5,0,0,0,0,0,0,1,1,1,0,0,0,0,11,-100,-5,9,1,0,0,0,0,0,0,0,0,0,3,13,-100,-7,11,7,3,1,0,1,3,7,11,-100,-100,-100,-100,-100,-100,-100,-100,-100,-101};
static int8_t lt3[]={-100,-100,-100,-100,-18,11,3,0,-100,-16,13,3,0,0,0,-100,-14,9,3,0,0,0,0,0,-100,-13,3,0,0,0,0,0,0,0,-100,-13,0,0,0,0,0,0,0,0,-100,-13,9,1,0,0,0,0,0,0,-100,-15,13,5,0,0,0,0,-100,-17,1,0,0,0,-100,-17,3,0,0,0,-100,-17,1,0,0,0,-100,-17,0,0,0,0,-100,-16,13,0,0,0,0,-100,-16,13,0,0,0,0,-100,-6,11,5,3,1,0,0,1,5,9,13,13,0,0,0,0,-100,-4,13,3,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,-100,-3,13,1,0,0,0,0,5,11,-1,13,11,3,0,0,0,0,0,0,-100,-2,13,1,0,0,0,0,7,-6,11,0,0,0,0,0,-100,-2,3,0,0,0,0,9,-8,11,0,0,0,0,-100,-1,9,0,0,0,0,7,-9,11,0,0,0,0,-100,-1,1,0,0,0,0,13,-9,11,0,0,0,0,-100,11,0,0,0,0,3,-10,11,0,0,0,0,-100,5,0,0,0,0,13,-10,11,0,0,0,0,-100,3,0,0,0,3,-11,11,0,0,0,0,-100,0,0,0,0,7,-11,11,0,0,0,0,-100,0,0,0,0,11,-11,11,0,0,0,0,-100,0,0,0,0,11,-11,11,0,0,0,0,-100,0,0,0,0,11,-11,11,0,0,0,0,-100,1,0,0,0,9,-11,11,0,0,0,0,-100,3,0,0,0,7,-11,11,0,0,0,0,-100,7,0,0,0,3,-11,11,0,0,0,0,-100,13,0,0,0,0,13,-10,13,0,0,0,0,-100,-1,3,0,0,0,5,-11,0,0,0,0,-100,-1,13,0,0,0,0,11,-10,0,0,0,0,-100,-2,7,0,0,0,1,13,-8,13,0,0,0,0,13,-100,-3,5,0,0,0,1,11,-7,5,0,0,0,0,9,-100,-4,5,0,0,0,0,3,9,13,-1,13,11,1,0,0,0,0,0,0,0,5,-100,-5,9,1,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,1,13,-100,-7,9,3,1,0,0,3,7,13,-1,11,1,0,1,5,7,11,-100,-18,13,-100,-100,-100,-100,-100,-100,-100,-100,-101};
static int8_t lt4[]={-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-7,13,5,1,1,0,1,3,9,13,-100,-6,5,0,0,0,0,0,0,0,0,0,9,-100,-4,11,1,0,0,3,11,-1,13,7,1,0,0,0,9,-100,-3,9,0,0,0,1,13,-5,3,0,0,1,-100,-2,9,0,0,0,0,13,-6,11,0,0,0,9,-100,-1,13,0,0,0,0,9,-8,0,0,0,1,-100,-1,5,0,0,0,0,13,-8,3,0,0,0,11,-100,-1,0,0,0,0,0,-8,13,1,0,0,0,5,-100,9,0,0,0,0,0,3,11,-4,13,11,1,0,0,0,0,1,-100,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-100,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,-100,1,0,0,0,1,13,-100,0,0,0,0,11,-100,0,0,0,0,11,-100,1,0,0,0,9,-100,3,0,0,0,7,-100,7,0,0,0,5,-100,13,0,0,0,0,11,-100,-1,5,0,0,0,1,13,-100,-1,13,1,0,0,0,1,13,-10,13,11,-100,-2,11,0,0,0,0,1,13,-8,13,1,1,-100,-3,11,0,0,0,0,0,7,13,-4,11,7,0,0,9,-100,-4,11,1,0,0,0,0,0,1,1,1,0,0,0,0,7,-100,-5,13,5,0,0,0,0,0,0,0,0,0,1,9,-100,-7,13,7,3,0,0,0,1,5,9,-100,-100,-100,-100,-100,-100,-100,-100,-100,-101};
static int8_t lt5[]={-100,-100,-100,-100,-9,13,9,3,1,0,1,5,13,-100,-8,7,0,0,0,0,0,0,0,3,-100,-7,3,0,0,0,0,0,0,0,0,1,-100,-6,5,0,0,0,0,0,0,0,0,0,3,-100,-5,13,0,0,3,13,9,1,0,0,0,0,11,-100,-5,9,0,0,13,-2,13,5,0,1,9,-100,-5,5,0,0,-100,-5,1,0,1,-100,-5,1,0,0,-100,-5,0,0,0,-100,-5,0,0,0,13,-100,-5,0,0,0,9,-100,-5,0,0,0,7,-100,-5,0,0,0,5,-100,-3,13,7,0,0,0,0,9,-1,13,11,13,-100,3,0,0,0,0,0,0,0,0,0,0,0,0,0,9,-100,3,0,0,0,0,0,0,0,0,0,0,0,0,0,9,-100,-1,11,13,-1,7,0,0,0,0,5,-1,13,11,13,-100,-5,0,0,0,0,11,-100,-5,0,0,0,0,11,-100,-5,0,0,0,0,11,-100,-5,0,0,0,0,11,-100,-5,0,0,0,0,11,-100,-5,0,0,0,0,11,-100,-5,0,0,0,0,11,-100,-5,0,0,0,0,11,-100,-5,0,0,0,0,11,-100,-5,0,0,0,0,11,-100,-5,0,0,0,0,11,-100,-5,0,0,0,0,11,-100,-5,0,0,0,0,11,-100,-5,0,0,0,0,11,-100,-5,0,0,0,0,11,-100,-5,0,0,0,0,7,-100,-3,13,5,0,0,0,0,0,7,13,-100,-1,5,0,0,0,0,0,0,0,0,0,0,0,1,11,-100,-1,7,1,0,0,0,0,0,0,0,0,1,3,7,13,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-101};
static int8_t lt6[]={-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-7,11,7,3,1,0,0,1,3,7,13,-4,13,7,5,7,11,-100,-5,11,3,0,0,0,0,0,0,0,0,0,0,5,13,13,5,0,0,0,0,0,-100,-4,9,0,0,0,5,13,-1,13,9,3,0,0,0,0,0,0,0,0,0,0,0,1,-100,-3,11,0,0,0,5,-6,5,0,0,0,0,0,11,13,9,3,0,9,-100,-2,13,0,0,0,0,13,-7,1,0,0,0,0,13,-100,-2,1,0,0,0,7,-8,7,0,0,0,0,7,-100,-2,0,0,0,0,9,-8,11,0,0,0,0,3,-100,-2,0,0,0,0,11,-9,0,0,0,0,1,-100,-2,0,0,0,0,11,-9,0,0,0,0,0,-100,-2,0,0,0,0,9,-8,13,0,0,0,0,1,-100,-2,3,0,0,0,5,-8,9,0,0,0,0,7,-100,-2,13,1,0,0,0,13,-7,5,0,0,0,0,13,-100,-3,11,0,0,0,3,13,-5,7,0,0,0,0,11,-100,-4,7,0,0,0,1,9,13,-1,13,7,0,0,0,1,11,-100,-4,13,0,0,0,0,0,0,0,0,0,0,0,5,13,-100,-4,13,0,0,0,0,0,0,1,3,5,9,-100,-2,13,5,0,11,-100,-1,13,1,0,0,-100,-1,3,0,0,0,1,7,13,-100,-1,0,0,0,0,0,0,0,1,3,7,9,11,13,-100,-1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,5,11,-100,-2,11,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,11,-100,-4,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,-100,-3,9,1,0,3,9,13,13,11,7,3,0,0,0,0,0,0,0,0,0,13,-100,-1,7,1,0,0,9,-8,11,7,3,0,0,0,0,0,5,-100,9,0,0,0,3,-13,11,1,0,0,1,-100,1,0,0,0,9,-15,0,0,0,-100,0,0,0,0,9,-14,13,0,0,1,-100,3,0,0,0,1,-14,7,0,0,5,-100,11,0,0,0,0,5,-12,11,0,0,0,13,-100,-1,11,1,0,0,0,1,7,11,13,-1,13,13,11,9,5,3,1,0,0,3,13,-100,-3,9,3,0,0,0,0,0,0,0,0,0,0,0,0,0,1,9,-100,-6,9,7,3,1,1,0,0,1,1,3,7,11,-100,-101};
static int8_t lt7[]={-100,-100,-100,-100,-4,13,7,1,9,-100,-2,13,7,0,0,0,3,-100,-1,7,0,0,0,0,0,5,-100,1,0,0,0,0,0,0,7,-100,3,0,0,0,0,0,0,7,-100,13,7,0,0,0,0,0,9,-100,-2,5,0,0,0,0,9,-100,-2,11,0,0,0,0,9,-100,-3,0,0,0,0,9,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,11,-5,11,5,1,0,1,7,-100,-3,0,0,0,0,11,-3,13,3,0,0,0,0,0,0,3,-100,-3,0,0,0,0,11,-2,7,0,0,0,0,0,0,0,0,0,9,-100,-3,0,0,0,0,11,11,3,0,5,11,-1,13,9,1,0,0,0,3,-100,-3,0,0,0,0,3,0,1,11,-6,1,0,0,0,13,-100,-3,0,0,0,0,0,5,-8,11,0,0,0,11,-100,-3,0,0,0,0,11,-10,0,0,0,7,-100,-3,0,0,0,0,13,-10,0,0,0,7,-100,-3,0,0,0,0,11,-10,0,0,0,7,-100,-3,0,0,0,0,11,-10,0,0,0,7,-100,-3,0,0,0,0,13,-10,0,0,0,9,-100,-3,0,0,0,0,13,-10,0,0,0,9,-100,-3,0,0,0,0,-11,0,0,0,11,-100,-3,0,0,0,1,-11,0,0,0,13,-100,-3,0,0,0,1,-11,0,0,0,13,-100,-3,0,0,0,3,-11,0,0,0,-100,-3,0,0,0,3,-11,0,0,0,-100,-3,0,0,0,3,-11,0,0,0,-100,-3,0,0,0,3,-10,13,0,0,0,13,-100,-3,0,0,0,1,-10,13,0,0,0,11,-100,-2,9,0,0,0,0,-10,11,0,0,0,5,-100,-1,7,0,0,0,0,0,5,13,-7,13,3,0,0,0,0,7,-100,5,0,0,0,0,0,0,0,0,3,-5,1,0,0,0,0,0,0,1,3,11,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-101};
static int8_t lt8[]={-100,-100,-100,-100,-3,5,1,0,3,11,-100,-2,7,0,0,0,0,0,11,-100,-2,1,0,0,0,0,0,5,-100,-2,0,0,0,0,0,0,5,-100,-2,5,0,0,0,0,0,11,-100,-3,7,0,0,3,9,-100,-4,13,-100,-100,-100,-100,-100,-100,-100,-4,11,3,0,9,-100,-2,9,3,0,0,0,9,-100,11,1,0,0,0,0,0,7,-100,1,0,0,0,0,0,0,7,-100,1,0,0,0,0,0,0,9,-100,-1,11,0,0,0,0,0,9,-100,-2,7,0,0,0,0,11,-100,-3,0,0,0,0,13,-100,-3,1,0,0,0,-100,-3,1,0,0,0,13,-100,-3,1,0,0,0,13,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,11,-100,-2,13,0,0,0,0,7,-100,-2,13,0,0,0,0,1,-100,-2,7,0,0,0,0,0,9,-100,7,0,0,0,0,0,0,0,0,3,-100,9,5,1,1,0,0,1,1,3,5,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-101};
static int8_t lt9[]={-100,-100,-100,-100,-6,5,0,1,5,13,-100,-5,5,0,0,0,0,1,-100,-5,1,0,0,0,0,0,13,-100,-5,0,0,0,0,0,0,-100,-5,5,0,0,0,0,3,-100,-6,7,1,0,5,13,-100,-100,-100,-100,-100,-100,-100,-100,-6,13,7,3,0,13,-100,-5,7,0,0,0,0,11,-100,-3,7,1,0,0,0,0,0,11,-100,-2,13,0,0,0,0,0,0,0,11,-100,-3,11,3,0,0,0,0,0,11,-100,-5,9,0,0,0,0,11,-100,-6,1,0,0,0,11,-100,-6,3,0,0,0,11,-100,-6,5,0,0,0,11,-100,-6,5,0,0,0,11,-100,-6,5,0,0,0,11,-100,-6,5,0,0,0,11,-100,-6,3,0,0,0,11,-100,-6,3,0,0,0,11,-100,-6,1,0,0,0,11,-100,-6,1,0,0,0,13,-100,-6,1,0,0,0,13,-100,-6,1,0,0,0,13,-100,-6,1,0,0,0,13,-100,-6,1,0,0,0,13,-100,-6,1,0,0,0,-100,-6,1,0,0,0,-100,-6,1,0,0,1,-100,-6,1,0,0,3,-100,-6,0,0,0,5,-100,-6,0,0,0,7,-100,-6,0,0,0,11,-100,11,0,0,0,5,5,0,0,5,-100,3,0,0,0,0,0,0,3,-100,0,0,0,0,0,0,0,13,-100,1,0,0,0,0,0,11,-100,13,3,0,0,3,11,-100,-100,-101};
static int8_t lt10[]={-100,-100,-100,-100,-6,9,1,13,-100,-3,13,5,0,0,0,11,-100,-1,13,5,0,0,0,0,0,11,-100,13,1,0,0,0,0,0,0,11,-100,3,0,0,0,0,0,0,0,11,-100,11,3,0,0,0,0,0,0,11,-100,-2,13,0,0,0,0,0,11,-100,-3,9,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-7,3,0,0,0,0,0,0,0,0,0,3,-100,-4,0,0,0,0,11,-7,5,0,0,0,0,0,0,0,1,7,13,-100,-4,0,0,0,0,11,-8,9,0,0,0,1,9,-100,-4,0,0,0,0,11,-7,13,1,0,0,7,-100,-4,0,0,0,0,11,-7,5,0,3,13,-100,-4,0,0,0,0,11,-6,5,0,5,-100,-4,0,0,0,0,11,-5,5,1,11,-100,-4,0,0,0,0,11,-3,11,3,1,-100,-4,0,0,0,0,11,-1,11,3,0,0,1,-100,-4,0,0,0,0,11,9,0,0,0,0,0,11,-100,-4,0,0,0,0,0,0,1,0,0,0,0,3,-100,-4,0,0,0,0,0,9,-1,11,1,0,0,0,9,-100,-4,0,0,0,0,5,-4,3,0,0,0,11,-100,-4,0,0,0,0,11,-4,13,1,0,0,0,13,-100,-4,0,0,0,0,11,-5,11,0,0,0,1,13,-100,-4,0,0,0,0,11,-6,3,0,0,0,3,-100,-4,0,0,0,0,11,-6,11,0,0,0,0,7,-100,-4,0,0,0,0,11,-7,3,0,0,0,0,7,-100,-3,13,0,0,0,0,11,-7,13,0,0,0,0,0,7,-100,-2,13,3,0,0,0,0,7,-7,9,0,0,0,0,0,0,3,11,-100,-1,1,0,0,0,0,0,0,0,0,1,3,-4,0,0,0,0,0,0,0,0,0,0,3,-100,-1,7,1,0,0,0,0,0,0,0,0,7,-4,3,0,0,0,0,0,0,1,1,5,9,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-101};
static int8_t lt11[]={-100,-100,-100,-6,11,7,11,-100,-4,11,3,0,0,5,-100,-3,5,0,0,0,0,5,-100,-1,11,1,0,0,0,0,0,7,-100,7,0,0,0,0,0,0,0,7,-100,0,0,0,0,0,0,0,0,9,-100,7,0,0,0,0,0,0,0,9,-100,-2,9,1,0,0,0,0,9,-100,-3,11,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,7,-100,-3,5,0,0,0,0,0,13,-100,-1,9,1,0,0,0,0,0,0,0,1,9,-100,-1,5,1,0,0,0,0,0,0,0,1,11,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-101};
static int8_t lt12[]={-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-5,11,5,0,-21,7,1,0,3,9,-100,-4,7,0,0,0,13,-4,11,5,1,1,0,1,7,-7,11,3,0,0,0,0,0,5,-100,-2,11,1,0,0,0,0,11,-2,9,1,0,0,0,0,0,0,0,9,-4,13,5,0,0,0,0,0,0,0,0,7,-100,13,3,0,0,0,0,0,0,7,13,3,0,0,0,1,0,0,0,0,0,0,7,-1,13,7,0,0,0,0,0,0,0,0,0,0,0,11,-100,3,0,0,0,0,0,0,0,1,0,0,5,11,-3,11,0,0,0,0,0,0,0,3,9,13,13,-3,7,0,0,0,0,5,-100,13,3,0,0,0,0,0,0,0,7,13,-6,3,0,0,0,0,0,9,-8,5,0,0,0,1,-100,-2,11,0,0,0,0,0,3,-8,9,0,0,0,0,7,-9,11,0,0,0,0,-100,-3,3,0,0,0,0,7,-8,13,0,0,0,0,-10,11,0,0,0,0,-100,-3,7,0,0,0,0,11,-9,0,0,0,0,-10,11,0,0,0,1,-100,-3,11,0,0,0,0,11,-9,0,0,0,0,-10,11,0,0,0,3,-100,-3,13,0,0,0,0,11,-8,13,0,0,0,0,-10,11,0,0,0,5,-100,-3,13,0,0,0,0,11,-8,11,0,0,0,0,-10,11,0,0,0,5,-100,-4,0,0,0,0,13,-8,9,0,0,0,0,-10,11,0,0,0,5,-100,-4,0,0,0,0,13,-8,11,0,0,0,0,-10,11,0,0,0,3,-100,-4,0,0,0,0,13,-8,11,0,0,0,0,-10,11,0,0,0,0,-100,-3,13,0,0,0,1,-9,13,0,0,0,0,-10,11,0,0,0,0,-100,-3,11,0,0,0,13,-10,0,0,0,0,-10,11,0,0,0,0,-100,-4,0,0,0,11,-10,0,0,0,0,-10,11,0,0,0,0,-100,-3,13,0,0,0,3,-9,13,0,0,0,0,13,-9,9,0,0,0,0,-100,-3,7,0,0,0,0,13,-8,11,0,0,0,0,11,-9,7,0,0,0,0,13,-100,-3,3,0,0,0,0,9,-8,5,0,0,0,0,5,-9,5,0,0,0,0,7,-100,-1,13,3,0,0,0,0,0,1,11,-6,9,0,0,0,0,0,0,3,-7,11,1,0,0,0,0,1,13,-100,11,0,0,0,0,0,0,0,0,0,3,-4,3,0,0,0,0,0,0,0,0,5,-5,1,0,0,0,0,0,0,0,0,9,-100,13,1,0,0,0,0,0,0,0,1,9,-4,1,0,0,0,0,0,0,0,1,5,-5,1,0,0,0,0,0,0,0,0,7,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-101};
static int8_t lt13[]={-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-3,13,7,3,0,13,-5,13,7,1,0,0,3,7,-100,-2,5,0,0,0,0,-4,13,3,0,0,0,0,0,0,0,7,-100,7,1,0,0,0,0,0,-3,7,0,0,0,0,0,0,0,0,0,0,13,-100,1,0,0,0,0,0,0,13,9,1,0,5,11,13,-1,11,3,0,0,0,0,9,-100,13,3,0,0,0,0,0,1,1,7,-7,3,0,0,0,3,-100,-2,1,0,0,0,0,7,-9,13,0,0,0,1,-100,-2,7,0,0,0,0,-11,1,0,0,0,-100,-2,9,0,0,0,0,-11,1,0,0,0,13,-100,-2,11,0,0,0,0,-11,3,0,0,0,13,-100,-2,11,0,0,0,0,-11,5,0,0,0,11,-100,-2,11,0,0,0,0,-11,3,0,0,0,11,-100,-2,11,0,0,0,0,-11,1,0,0,0,11,-100,-2,11,0,0,0,0,-11,0,0,0,0,11,-100,-2,11,0,0,0,0,-11,0,0,0,0,11,-100,-2,11,0,0,0,0,-11,1,0,0,0,11,-100,-2,11,0,0,0,0,-11,3,0,0,0,11,-100,-2,11,0,0,0,0,-11,3,0,0,0,11,-100,-2,11,0,0,0,0,13,-10,5,0,0,0,11,-100,-2,11,0,0,0,0,13,-10,5,0,0,0,11,-100,-2,11,0,0,0,0,9,-10,5,0,0,0,9,-100,-2,7,0,0,0,0,5,-10,3,0,0,0,7,-100,-1,13,1,0,0,0,0,1,11,-7,11,5,0,0,0,0,0,3,9,-100,-1,1,0,0,0,0,0,0,0,0,3,-4,3,0,0,0,0,0,0,0,0,0,3,-100,-1,0,0,0,0,0,0,0,0,0,3,-4,5,1,0,0,0,0,0,0,0,0,1,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-101};
static int8_t lt14[]={-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-7,13,7,3,1,0,0,1,5,9,-100,-6,7,0,0,0,0,0,0,0,0,0,1,7,-100,-4,11,1,0,0,1,9,13,-1,13,11,5,0,0,0,3,13,-100,-3,11,0,0,0,3,13,-6,13,1,0,0,0,13,-100,-2,11,0,0,0,1,13,-8,13,0,0,0,5,-100,-2,0,0,0,0,9,-10,5,0,0,0,11,-100,-1,7,0,0,0,1,-11,13,0,0,0,1,-100,-1,1,0,0,0,5,-12,1,0,0,0,9,-100,11,0,0,0,0,11,-12,5,0,0,0,3,-100,7,0,0,0,0,13,-12,9,0,0,0,0,-100,3,0,0,0,0,13,-12,11,0,0,0,0,-100,1,0,0,0,0,-13,11,0,0,0,1,-100,0,0,0,0,0,-13,11,0,0,0,1,-100,0,0,0,0,0,13,-12,11,0,0,0,0,-100,1,0,0,0,0,13,-12,9,0,0,0,1,-100,5,0,0,0,0,11,-12,5,0,0,0,3,-100,9,0,0,0,0,9,-12,3,0,0,0,7,-100,-1,1,0,0,0,5,-12,0,0,0,0,11,-100,-1,9,0,0,0,3,-11,13,0,0,0,3,-100,-2,3,0,0,0,13,-10,7,0,0,0,11,-100,-2,13,1,0,0,3,-9,13,0,0,0,7,-100,-3,13,1,0,0,3,13,-6,13,1,0,0,5,-100,-4,13,1,0,0,1,7,13,-3,9,0,0,0,7,-100,-6,7,0,0,0,0,0,0,0,0,0,3,11,-100,-7,13,7,3,0,0,0,0,3,11,-100,-100,-100,-100,-100,-100,-100,-100,-100,-101};
static int8_t lt15[]={-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-6,11,7,-100,-4,11,3,0,0,7,11,5,3,0,0,0,1,5,9,-100,-2,11,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,-100,13,5,0,0,0,0,0,0,0,1,7,11,13,-1,13,3,0,0,0,5,-100,1,0,0,0,0,0,0,0,3,-7,7,0,0,0,7,-100,1,0,0,0,0,0,0,0,11,-8,7,0,0,0,13,-100,-1,11,5,0,0,0,0,0,11,-9,1,0,0,3,-100,-3,7,0,0,0,0,11,-9,5,0,0,0,11,-100,-3,9,0,0,0,0,11,-9,9,0,0,0,5,-100,-3,11,0,0,0,0,11,-9,11,0,0,0,0,13,-100,-3,11,0,0,0,0,11,-9,13,0,0,0,0,11,-100,-3,13,0,0,0,0,11,-10,0,0,0,0,11,-100,-3,13,0,0,0,0,11,-10,0,0,0,0,11,-100,-3,13,0,0,0,0,11,-10,0,0,0,0,11,-100,-4,0,0,0,0,11,-10,0,0,0,0,13,-100,-4,0,0,0,0,11,-10,0,0,0,0,13,-100,-4,0,0,0,0,13,-10,0,0,0,0,-100,-4,0,0,0,9,-11,0,0,0,3,-100,-4,0,0,0,3,-11,0,0,0,9,-100,-4,0,0,0,0,13,-10,0,0,0,13,-100,-4,0,0,0,0,11,-9,5,0,0,0,-100,-4,0,0,0,0,11,-8,7,0,0,0,7,-100,-4,0,0,0,0,5,-7,9,0,0,0,3,-100,-4,0,0,0,0,0,3,11,13,-3,9,0,0,0,1,-100,-4,0,0,0,0,5,7,0,0,0,1,1,0,0,0,1,13,-100,-4,0,0,0,0,11,-2,9,5,1,0,0,3,7,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,7,-100,-4,0,0,0,0,3,-100,-3,7,0,0,0,0,0,9,-100,-1,5,0,0,0,0,0,0,0,0,0,0,5,-100,-1,3,1,0,0,0,0,0,0,0,0,0,5,-100,-101};
static int8_t lt16[]={-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-19,7,11,-100,-6,11,5,1,0,0,1,5,11,-4,5,0,3,-100,-4,13,3,0,0,0,0,0,0,0,0,1,11,13,5,0,0,1,-100,-3,13,1,0,0,1,5,11,-3,11,3,0,0,0,0,0,3,-100,-2,13,1,0,0,3,-8,3,0,0,0,0,5,-100,-1,13,1,0,0,0,13,-8,7,0,0,0,0,5,-100,-1,3,0,0,0,5,-9,11,0,0,0,0,7,-100,9,0,0,0,0,11,-9,13,0,0,0,0,9,-100,3,0,0,0,0,-10,13,0,0,0,0,9,-100,1,0,0,0,1,-11,0,0,0,0,9,-100,0,0,0,0,3,-11,0,0,0,0,11,-100,0,0,0,0,5,-11,0,0,0,0,11,-100,0,0,0,0,9,-11,0,0,0,0,11,-100,0,0,0,0,11,-11,0,0,0,0,11,-100,0,0,0,0,11,-11,0,0,0,0,11,-100,1,0,0,0,11,-11,0,0,0,0,11,-100,3,0,0,0,9,-11,0,0,0,0,11,-100,7,0,0,0,5,-11,0,0,0,0,11,-100,11,0,0,0,0,-11,0,0,0,0,11,-100,-1,1,0,0,0,7,-9,11,0,0,0,0,11,-100,-1,9,0,0,0,0,13,-8,9,0,0,0,0,11,-100,-2,3,0,0,0,1,13,-7,5,0,0,0,0,11,-100,-2,13,3,0,0,0,1,9,13,-3,13,7,0,0,0,0,0,11,-100,-4,9,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,11,-100,-6,11,7,3,1,0,0,5,9,-1,13,0,0,0,0,11,-100,-16,0,0,0,0,13,-100,-16,0,0,0,0,13,-100,-16,0,0,0,0,13,-100,-16,0,0,0,0,13,-100,-15,13,0,0,0,0,13,-100,-15,9,0,0,0,0,11,-100,-15,1,0,0,0,0,5,-100,-12,7,5,1,0,0,0,0,0,0,1,3,9,-100,-11,13,1,0,0,0,0,0,0,0,0,0,0,7,-100,-101};
static int8_t lt17[]={-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-3,13,5,0,3,-5,9,3,1,0,0,3,-100,-1,11,3,0,0,0,0,-3,7,0,0,0,0,0,0,0,5,-100,9,0,0,0,0,0,0,-2,3,0,0,0,0,0,0,0,0,1,-100,0,0,0,0,0,0,0,11,3,0,0,0,0,0,0,0,0,0,1,-100,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,-100,-1,7,0,0,0,0,0,0,7,13,-2,11,7,3,1,3,11,-100,-2,3,0,0,0,0,3,-100,-2,7,0,0,0,0,9,-100,-2,11,0,0,0,0,13,-100,-2,13,0,0,0,0,-100,-3,0,0,0,0,-100,-3,0,0,0,0,13,-100,-3,0,0,0,0,13,-100,-3,0,0,0,0,13,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,11,-100,-3,0,0,0,0,9,-100,-1,13,3,0,0,0,0,1,13,-100,3,0,0,0,0,0,0,0,0,0,0,13,-100,5,1,0,0,0,0,0,0,0,0,3,13,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-101};
static int8_t lt18[]={-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-3,13,7,1,0,1,3,9,11,9,9,13,-100,-2,13,1,0,0,0,0,0,0,0,0,0,5,-100,-2,1,0,5,13,-1,13,11,3,0,0,0,1,-100,-1,9,0,1,-6,9,0,0,0,13,-100,-1,3,0,9,-7,11,1,0,9,-100,-1,0,0,-9,13,1,5,-100,-1,0,0,11,-9,13,11,-100,13,0,0,1,13,-100,13,0,0,0,1,9,-100,-1,0,0,0,0,0,0,5,9,-100,-1,5,0,0,0,0,0,0,0,1,7,13,-100,-1,13,0,0,0,0,0,0,0,0,0,0,9,-100,-2,13,1,0,0,0,0,0,0,0,0,0,7,-100,-4,11,5,0,0,0,0,0,0,0,0,7,-100,-7,9,5,0,0,0,0,0,0,13,-100,-9,13,7,0,0,0,0,7,-100,-11,13,3,0,0,3,-100,-13,0,0,0,-100,7,5,-11,1,0,0,-100,1,0,5,-10,1,0,3,-100,0,0,0,3,-9,0,0,9,-100,1,0,0,0,3,13,-6,5,0,3,-100,7,0,0,0,0,1,7,11,13,13,9,3,0,1,13,-100,13,0,0,0,0,0,0,0,0,0,0,0,3,13,-100,-1,5,3,9,7,3,1,0,0,1,5,11,-100,-100,-100,-100,-100,-100,-100,-100,-100,-101};
static int8_t lt19[]={-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-6,5,0,5,-100,-5,13,0,0,0,7,-100,-5,7,0,0,0,9,-100,-5,3,0,0,0,13,-100,-4,11,0,0,0,0,-100,-4,3,0,0,0,0,-100,-3,5,0,0,0,0,1,-100,-2,3,0,0,0,0,0,7,-100,13,1,0,0,0,0,0,0,1,11,-100,3,0,0,0,0,0,0,0,0,0,0,0,0,0,5,13,-100,1,0,0,0,0,0,0,0,0,0,0,0,0,0,5,-100,-3,9,0,0,0,0,7,13,-100,-4,0,0,0,0,11,-100,-4,1,0,0,0,11,-100,-4,1,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,0,0,0,0,11,-100,-4,1,0,0,0,11,-100,-4,3,0,0,0,11,-100,-4,5,0,0,0,11,-100,-4,3,0,0,0,11,-100,-4,3,0,0,0,11,-100,-4,1,0,0,0,9,-100,-4,0,0,0,0,5,-100,-4,0,0,0,0,1,-100,-4,1,0,0,0,0,11,-100,-4,3,0,0,0,0,1,13,-1,9,5,1,3,-100,-4,9,0,0,0,0,0,0,0,0,0,0,5,-100,-5,5,0,0,0,0,0,0,0,0,5,-100,-6,9,3,1,0,0,1,5,11,-100,-100,-100,-100,-100,-100,-100,-100,-100,-101};
static int8_t lt20[]={-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-4,13,9,1,0,13,-11,11,5,1,5,-100,-2,11,3,0,0,0,0,11,-8,11,5,0,0,0,0,1,-100,13,3,0,0,0,0,0,0,11,-6,7,1,0,0,0,0,0,0,7,-100,5,0,0,0,0,0,0,0,11,-5,5,0,0,0,0,0,0,0,0,11,-100,11,1,0,0,0,0,0,0,11,-5,9,0,0,0,0,0,0,0,0,11,-100,-2,11,1,0,0,0,0,11,-6,13,7,3,0,0,0,0,0,11,-100,-3,9,0,0,0,0,11,-9,9,0,0,0,0,11,-100,-3,11,0,0,0,0,11,-10,0,0,0,0,11,-100,-3,13,0,0,0,0,11,-10,0,0,0,0,11,-100,-4,0,0,0,0,11,-10,0,0,0,0,11,-100,-4,0,0,0,0,11,-10,0,0,0,0,11,-100,-4,0,0,0,0,11,-10,0,0,0,0,11,-100,-4,0,0,0,0,11,-10,0,0,0,0,11,-100,-4,0,0,0,0,11,-10,0,0,0,0,11,-100,-4,0,0,0,0,11,-10,0,0,0,0,11,-100,-4,0,0,0,0,11,-10,0,0,0,0,11,-100,-4,0,0,0,0,11,-10,0,0,0,0,11,-100,-4,1,0,0,0,11,-9,13,0,0,0,0,11,-100,-4,1,0,0,0,11,-9,5,0,0,0,0,11,-100,-4,5,0,0,0,7,-8,7,0,0,0,0,0,11,-100,-4,7,0,0,0,0,13,-5,13,5,0,0,0,0,0,0,11,-100,-4,13,0,0,0,0,1,11,-1,13,11,5,0,0,7,5,0,0,0,0,3,13,-100,-5,3,0,0,0,0,0,0,0,0,0,3,13,-2,0,0,0,0,0,0,7,-100,-5,11,0,0,0,0,0,0,0,1,9,-4,0,0,0,0,0,0,0,-100,-6,13,5,1,0,1,3,9,-6,3,1,3,7,9,11,13,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-101};
static int8_t lt21[]={-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-6,13,9,1,3,13,-5,13,1,0,0,0,0,1,3,9,-100,1,0,0,0,0,0,0,0,0,0,1,-5,1,0,0,0,0,0,0,0,0,9,-100,3,0,0,0,0,0,0,0,0,0,5,-5,3,0,0,0,0,0,0,0,0,13,-100,-1,9,3,0,0,0,0,0,5,13,-7,13,9,0,0,0,0,1,11,-100,-3,7,0,0,0,0,-11,7,0,0,5,-100,-4,0,0,0,0,9,-10,9,0,5,-100,-4,5,0,0,0,1,-10,5,0,11,-100,-4,11,0,0,0,0,9,-9,0,1,-100,-5,1,0,0,0,3,-8,9,0,7,-100,-5,7,0,0,0,0,13,-7,3,0,13,-100,-5,13,0,0,0,0,7,-6,13,0,3,-100,-6,3,0,0,0,1,-6,7,0,11,-100,-6,9,0,0,0,0,9,-5,0,1,-100,-6,13,0,0,0,0,3,-4,7,0,7,-100,-7,5,0,0,0,0,11,-3,1,0,13,-100,-7,11,0,0,0,0,1,-2,9,0,5,-100,-8,1,0,0,0,0,5,-1,1,0,11,-100,-8,7,0,0,0,0,0,0,0,3,-100,-8,13,0,0,0,0,0,0,0,9,-100,-9,3,0,0,0,0,0,1,-100,-9,9,0,0,0,0,0,7,-100,-10,0,0,0,0,0,13,-100,-10,5,0,0,0,1,-100,-10,13,1,0,0,7,-100,-11,11,1,5,-100,-100,-100,-100,-100,-100,-100,-100,-100,-101};
static int8_t lt22[]={-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-29,13,-100,5,0,0,1,1,1,3,3,5,7,-3,7,0,0,0,0,0,0,0,0,0,11,-4,3,0,0,0,0,0,0,1,3,11,-100,5,0,0,0,0,0,0,0,1,7,-3,9,0,0,0,0,0,0,0,0,1,11,-4,9,1,0,0,0,0,0,0,1,13,-100,-1,13,5,0,0,0,0,0,13,-5,13,5,0,0,0,0,0,3,-8,9,0,0,0,0,5,-100,-3,5,0,0,0,0,-8,11,0,0,0,0,9,-9,9,0,0,9,-100,-4,1,0,0,0,13,-8,11,0,0,0,3,-10,0,3,-100,-4,7,0,0,0,11,-8,13,0,0,0,0,13,-8,9,0,11,-100,-4,13,0,0,0,7,-8,11,0,0,0,0,7,-8,1,1,-100,-5,5,0,0,1,-8,7,0,0,0,0,3,-8,5,11,-100,-5,11,0,0,0,9,-7,1,0,0,0,0,0,13,-100,-6,0,0,0,3,-6,9,0,3,13,0,0,0,7,-6,5,-100,-6,3,0,0,0,13,-5,1,0,11,-1,3,0,0,3,-6,1,-100,-6,9,0,0,0,9,-4,11,0,3,-2,7,0,0,0,13,-4,11,1,-100,-6,13,0,0,0,3,-4,5,0,9,-2,13,0,0,0,7,-4,5,3,-100,-7,3,0,0,0,13,-3,1,1,-4,0,0,0,1,-4,1,7,-100,-7,9,0,0,0,5,-2,13,0,7,-4,5,0,0,0,9,-2,9,0,13,-100,-8,1,0,0,0,13,-1,7,1,-5,11,0,0,0,1,-2,1,3,-100,-8,7,0,0,0,1,13,1,5,-6,1,0,0,0,13,9,0,9,-100,-8,13,0,0,0,0,0,0,11,-6,7,0,0,0,1,0,1,-100,-9,3,0,0,0,0,3,-7,13,0,0,0,0,0,9,-100,-9,9,0,0,0,0,5,-8,3,0,0,0,0,13,-100,-9,13,0,0,0,0,11,-8,9,0,0,0,1,-100,-10,3,0,0,0,-10,3,0,0,5,-100,-10,11,0,0,7,-10,13,1,1,13,-100,-11,11,11,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-101};
static int8_t lt23[]={-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-16,13,13,-1,13,9,5,1,0,1,5,13,-100,-1,3,0,0,0,0,0,0,0,0,1,7,-4,1,0,0,0,0,0,0,0,0,0,1,-100,-1,1,0,0,0,0,0,0,0,0,1,11,-4,7,0,0,0,0,0,0,0,0,0,5,-100,-2,13,7,0,0,0,0,0,0,13,-6,13,5,0,0,0,0,5,9,13,-100,-4,13,3,0,0,0,0,3,-7,13,0,0,5,13,-100,-6,3,0,0,0,0,5,-6,7,0,7,-100,-7,3,0,0,0,0,11,-4,11,0,9,-100,-8,5,0,0,0,1,-3,11,0,7,-100,-9,3,0,0,0,1,11,11,0,7,-100,-9,13,1,0,0,0,0,0,7,-100,-10,13,1,0,0,0,0,9,-100,-11,11,0,0,0,0,1,-100,-12,0,0,0,0,0,7,-100,-11,9,0,0,0,0,0,0,11,-100,-10,9,0,5,13,3,0,0,0,1,13,-100,-9,11,0,3,-2,13,1,0,0,0,1,13,-100,-8,11,0,3,13,-3,13,1,0,0,0,1,13,-100,-7,9,0,1,13,-5,11,0,0,0,0,1,13,-100,-6,9,0,0,7,-7,9,0,0,0,0,1,11,-100,-5,5,0,0,0,13,-8,7,0,0,0,0,0,9,-100,-3,13,3,0,0,0,0,13,-8,13,0,0,0,0,0,0,5,-100,9,3,1,0,0,0,0,0,0,1,11,-6,11,1,0,0,0,0,0,0,0,1,3,13,-100,3,0,0,0,0,0,0,0,0,0,3,-6,11,5,3,1,0,0,0,1,1,3,5,-100,13,13,13,13,-100,-100,-100,-100,-100,-100,-100,-100,-100,-101};
static int8_t lt24[]={-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,-1,13,13,-2,13,13,11,11,11,13,-6,11,11,11,11,11,11,11,11,13,-100,3,0,0,0,0,0,0,0,0,0,0,3,-4,1,0,0,0,0,0,0,0,0,1,-100,7,0,0,0,0,0,0,0,0,0,0,5,-4,3,0,0,0,0,0,0,0,0,9,-100,-2,11,5,0,0,0,0,0,0,11,-6,11,0,0,0,0,1,7,13,-100,-4,11,0,0,0,0,0,-8,9,0,0,1,13,-100,-5,7,0,0,0,0,13,-7,9,0,0,13,-100,-6,1,0,0,0,9,-7,5,0,5,-100,-6,7,0,0,0,3,-7,1,0,13,-100,-6,13,0,0,0,0,13,-5,11,0,1,-100,-7,5,0,0,0,7,-5,7,0,7,-100,-7,11,0,0,0,3,-5,1,0,11,-100,-8,1,0,0,0,13,-3,13,0,3,-100,-8,7,0,0,0,7,-3,7,0,9,-100,-8,13,0,0,0,3,-3,1,1,-100,-9,5,0,0,0,13,-1,9,0,7,-100,-9,11,0,0,0,3,13,1,0,13,-100,-10,1,0,0,0,0,0,3,-100,-10,9,0,0,0,0,0,9,-100,-11,1,0,0,0,1,-100,-11,5,0,0,0,7,-100,-11,9,0,0,0,13,-100,-11,13,0,0,3,-100,-12,0,0,9,-100,-12,0,1,-100,-11,13,0,7,-100,-11,7,0,11,-100,-11,1,0,-100,-2,7,0,1,7,-4,11,0,3,-100,-1,11,0,0,0,0,1,9,13,11,1,0,9,-100,-1,3,0,0,0,0,0,0,0,0,0,3,-100,-1,0,0,0,0,0,0,0,0,0,1,13,-100,-1,7,0,0,0,0,0,0,0,1,13,-100,-2,9,3,0,0,1,3,7,-100,-101};
static int8_t *lt[]={lt0,lt1,lt2,lt3,lt0,lt5,lt0,lt7,lt8,lt9,lt10,lt11,lt12,lt13,lt14,lt15,lt16,lt17,lt18,lt19,lt20,lt21,lt22,lt23,lt24,};

214
ext/rucaptcha/rucaptcha.c Normal file
View File

@ -0,0 +1,214 @@
// http://github.com/ITikhonov/captcha
const int gifsize;
void captcha(unsigned char im[70*200], unsigned char l[6]);
void makegif(unsigned char im[70*200], unsigned char gif[gifsize], int style);
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <ruby.h>
#include "font.h"
#include "colors.h"
static int8_t *lt[];
const int gifsize=17646;
void makegif(unsigned char im[70*200], unsigned char gif[gifsize], int style) {
// tag ; widthxheight ; GCT:0:0:7 ; bgcolor + aspect // GCT
// Image Separator // left x top // widthxheight // Flags
// LZW code size
srand(time(NULL));
int color_len = (int) sizeof(colors) / sizeof(colors[0]);
int color_idx = rand() % color_len;
if (style == 0) {
color_idx = 0;
}
memcpy(gif,colors[color_idx],13+48+10+1);
int x,y;
unsigned char *i=im;
unsigned char *p=gif+13+48+10+1;
for(y=0;y<70;y++) {
*p++=250; // Data length 5*50=250
for(x=0;x<50;x++)
{
unsigned char a=i[0]>>4,b=i[1]>>4,c=i[2]>>4,d=i[3]>>4;
p[0]=16|(a<<5); // bbb10000
p[1]=(a>>3)|64|(b<<7); // b10000xb
p[2]=b>>1; // 0000xbbb
p[3]=1|(c<<1); // 00xbbbb1
p[4]=4|(d<<3); // xbbbb100
i+=4;
p+=5;
}
}
// Data length // End of LZW (b10001) // Terminator // GIF End
memcpy(gif+gifsize-4,"\x01" "\x11" "\x00" ";",4);
}
static const int8_t sw[200]={0, 4, 8, 12, 16, 20, 23, 27, 31, 35, 39, 43, 47, 50, 54, 58, 61, 65, 68, 71, 75, 78, 81, 84, 87, 90, 93, 96, 98, 101, 103, 105, 108, 110, 112, 114, 115, 117, 119, 120, 121, 122, 123, 124, 125, 126, 126, 127, 127, 127, 127, 127, 127, 127, 126, 126, 125, 124, 123, 122, 121, 120, 119, 117, 115, 114, 112, 110, 108, 105, 103, 101, 98, 96, 93, 90, 87, 84, 81, 78, 75, 71, 68, 65, 61, 58, 54, 50, 47, 43, 39, 35, 31, 27, 23, 20, 16, 12, 8, 4, 0, -4, -8, -12, -16, -20, -23, -27, -31, -35, -39, -43, -47, -50, -54, -58, -61, -65, -68, -71, -75, -78, -81, -84, -87, -90, -93, -96, -98, -101, -103, -105, -108, -110, -112, -114, -115, -117, -119, -120, -121, -122, -123, -124, -125, -126, -126, -127, -127, -127, -127, -127, -127, -127, -126, -126, -125, -124, -123, -122, -121, -120, -119, -117, -115, -114, -112, -110, -108, -105, -103, -101, -98, -96, -93, -90, -87, -84, -81, -78, -75, -71, -68, -65, -61, -58, -54, -50, -47, -43, -39, -35, -31, -27, -23, -20, -16, -12, -8, -4};
#define MAX(x,y) ((x>y)?(x):(y))
static int letter(int n, int pos, unsigned char im[70*200], unsigned char swr[200], uint8_t s1, uint8_t s2) {
int8_t *p=lt[n];
unsigned char *r=im+200*16+pos;
unsigned char *i=r;
int sk1=s1+pos;
int sk2=s2+pos;
int mpos=pos;
int row=0;
for(;*p!=-101;p++) {
if(*p<0) {
if(*p==-100) { r+=200; i=r; sk1=s1+pos; row++; continue; }
i+=-*p;
continue;
}
if(sk1>=200) sk1=sk1%200;
int skew=sw[sk1]/16;
sk1+=(swr[pos+i-r]&0x1)+1;
if(sk2>=200) sk2=sk2%200;
int skewh=sw[sk2]/70;
sk2+=(swr[row]&0x1);
unsigned char *x=i+skew*200+skewh;
mpos=MAX(mpos,pos+i-r);
if((x-im)<70*200) *x=(*p)<<4;
i++;
}
return mpos;
}
#define NDOTS 100
uint32_t dr[NDOTS];
static void line(unsigned char im[70*200], unsigned char swr[200], uint8_t s1) {
int x;
int sk1=s1;
for(x=0;x<199;x++) {
if(sk1>=200) sk1=sk1%200;
int skew=sw[sk1]/16;
sk1+=swr[x]&0x3+1;
unsigned char *i= im+(200*(45+skew)+x);
i[0]=0; i[1]=0; i[200]=0; i[201]=0;
}
}
static void dots(unsigned char im[70*200]) {
int n;
for(n=0;n<NDOTS;n++) {
uint32_t v=dr[n];
unsigned char *i=im+v%(200*67);
i[0]=0xff;
i[1]=0xff;
i[2]=0xff;
i[200]=0xff;
i[201]=0xff;
i[202]=0xff;
}
}
static void blur(unsigned char im[70*200]) {
unsigned char *i=im;
int x,y;
for(y=0;y<68;y++) {
for(x=0;x<198;x++) {
unsigned int c11=*i,c12=i[1],c21=i[200],c22=i[201];
*i++=((c11+c12+c21+c22)/4);
}
}
}
static void filter(unsigned char im[70*200]) {
unsigned char om[70*200];
unsigned char *i=im;
unsigned char *o=om;
memset(om,0xff,sizeof(om));
int x,y;
for(y=0;y<70;y++) {
for(x=4;x<200-4;x++) {
if(i[0]>0xf0 && i[1]<0xf0) { o[0]=0; o[1]=0; }
else if(i[0]<0xf0 && i[1]>0xf0) { o[0]=0; o[1]=0; }
i++;
o++;
}
}
memmove(im,om,sizeof(om));
}
static const char *letters="abcdafahijklmnopqrstuvwxyz";
void captcha(unsigned char im[70*200], unsigned char l[6]) {
unsigned char swr[200];
uint8_t s1,s2;
int f=open("/dev/urandom",O_RDONLY);
read(f,l,5); read(f,swr,200); read(f,dr,sizeof(dr)); read(f,&s1,1); read(f,&s2,1);
close(f);
memset(im,0xff,200*70); s1=s1&0x7f; s2=s2&0x3f; l[0]%=25; l[1]%=25; l[2]%=25; l[3]%=25; l[4]%=25; l[5]=0;
int p=30; p=letter(l[0],p,im,swr,s1,s2); p=letter(l[1],p,im,swr,s1,s2); p=letter(l[2],p,im,swr,s1,s2); p=letter(l[3],p,im,swr,s1,s2); letter(l[4],p,im,swr,s1,s2);
line(im,swr,s1); dots(im); // blur(im); // filter(im);
l[0]=letters[l[0]]; l[1]=letters[l[1]]; l[2]=letters[l[2]]; l[3]=letters[l[3]]; l[4]=letters[l[4]];
}
// #ifdef CAPTCHA
//
// int main() {
// char l[6];
// unsigned char im[70*200];
// unsigned char gif[gifsize];
//
// captcha(im,l);
// makegif(im,gif);
//
// write(1,gif,gifsize);
// write(2,l,5);
//
// return 0;
// }
//
// #endif
VALUE RuCaptcha = Qnil;
void Init_rucaptcha();
VALUE create(VALUE self, VALUE style);
void Init_rucaptcha() {
RuCaptcha = rb_define_module("RuCaptcha");
rb_define_singleton_method(RuCaptcha, "create", create, 1);
}
VALUE create(VALUE self, VALUE style) {
char l[6];
unsigned char im[80*200];
unsigned char gif[gifsize];
int i_style = FIX2INT(style);
captcha(im, l);
makegif(im, gif, i_style);
VALUE result = rb_ary_new2(2);
rb_ary_push(result, rb_str_new2(l));
rb_ary_push(result, rb_str_new(gif, gifsize));
return result;
}

View File

@ -1,12 +1,12 @@
require 'rails' require 'rails'
require 'action_controller' require 'action_controller'
require 'active_support/all' require 'active_support/all'
require_relative 'rucaptcha/rucaptcha'
require_relative 'rucaptcha/version' require_relative 'rucaptcha/version'
require_relative 'rucaptcha/configuration' require_relative 'rucaptcha/configuration'
require_relative 'rucaptcha/controller_helpers' require_relative 'rucaptcha/controller_helpers'
require_relative 'rucaptcha/view_helpers' require_relative 'rucaptcha/view_helpers'
require_relative 'rucaptcha/cache' require_relative 'rucaptcha/cache'
require_relative 'rucaptcha/captcha'
require_relative 'rucaptcha/engine' require_relative 'rucaptcha/engine'
module RuCaptcha module RuCaptcha
@ -14,12 +14,8 @@ module RuCaptcha
def config def config
return @config if defined?(@config) return @config if defined?(@config)
@config = Configuration.new @config = Configuration.new
@config.len = 4
@config.font_size = 45
@config.implode = 0.3
@config.cache_limit = 100
@config.expires_in = 2.minutes
@config.style = :colorful @config.style = :colorful
@config.expires_in = 2.minutes
if Rails.application if Rails.application
@config.cache_store = Rails.application.config.cache_store @config.cache_store = Rails.application.config.cache_store
else else
@ -31,6 +27,11 @@ module RuCaptcha
def configure(&block) def configure(&block)
config.instance_exec(&block) config.instance_exec(&block)
end end
def generate()
style = config.style == :colorful ? 1 : 0
self.create(style)
end
end end
end end

View File

@ -8,46 +8,4 @@ module RuCaptcha
@cache @cache
end end
end end
# File Cache
module Cache
def self.prepended(base)
class << base
prepend ClassMethods
end
end
module ClassMethods
def create(code)
file_cache.fetch(code, expires_in: 1.days) do
super(code)
end
end
def random_chars
if cached_codes.length >= RuCaptcha.config.cache_limit
return cached_codes.sample
end
code = super
cached_codes << code
code
end
def file_cache
return @file_cache if defined?(@file_cache)
cache_path = Rails.root.join('tmp', 'cache', 'rucaptcha')
FileUtils.mkdir_p(cache_path) unless File.exist? cache_path
@file_cache = ActiveSupport::Cache::FileStore.new(cache_path)
# clear expired captcha cache files on Process restart
@file_cache.cleanup
@file_cache
end
def cached_codes
@cached_codes ||= []
end
end
end
end end

View File

@ -1,125 +0,0 @@
require 'open3'
module RuCaptcha
class Captcha
class << self
# Genrate ranom RGB color
def random_color
if RuCaptcha.config.style == :colorful
color1 = rand(56) + 15
color2 = rand(10) + 140
color = [color1, color2, rand(15)]
color.shuffle!
color
else
color_seed = rand(40) + 10
[color_seed, color_seed, color_seed]
end
end
# Genrate random Captcha code
def random_chars
chars = SecureRandom.hex(RuCaptcha.config.len / 2).downcase
chars.gsub!(/[0ol1]/i, (rand(8) + 2).to_s)
chars
end
# Create Captcha image by code
def create(code)
chars = code.split('')
full_width = RuCaptcha.config.font_size * chars.size
full_height = RuCaptcha.config.font_size
size = "#{full_width}x#{full_height}"
return convert_for_windows(size, code) if Gem.win_platform?
opts = command_line_opts(chars, full_width)
convert(size, opts)
end
private
def command_line_opts(chars, full_width)
font_size = RuCaptcha.config.font_size
all_left = 20
half_width = full_width / 2
text_top = 0
text_left = 0 - (font_size * 0.28).to_i
text_width = font_size + text_left
opts = { text: [], line: [] }
rgbs = uniq_rgbs_for_each_chars(chars)
chars.each_with_index do |char, i|
rgb = RuCaptcha.config.style == :colorful ? rgbs[i] : rgbs[0]
text_color = "rgba(#{rgb.join(',')}, 1)"
line_color = "rgba(#{rgb.join(',')}, 0.6)"
opts[:text] << %(-fill '#{text_color}' -draw 'text #{(text_left + text_width) * i + all_left},#{text_top} "#{char}"')
left_y = rand_line_top(text_top, font_size)
right_x = half_width + (half_width * 0.3).to_i
right_y = rand_line_top(text_top, font_size)
opts[:line] << %(-draw 'stroke #{line_color} line #{rand(10)},#{left_y} #{right_x},#{right_y}')
end
opts
end
def convert(size, opts)
stroke_width = (RuCaptcha.config.font_size * 0.05).to_i + 1
command = <<-CODE
convert -size #{size} \
-strokewidth #{stroke_width} \
#{opts[:line].join(' ')} \
-pointsize #{RuCaptcha.config.font_size} -weight 500 \
#{opts[:text].join(' ')} \
-wave #{rand(2) + 3}x#{rand(2) + 1} \
-rotate #{rand(10) - 5} \
-gravity NorthWest -sketch 1x10+#{rand(2)} \
-fill none \
-implode #{RuCaptcha.config.implode} -trim label:- png:-
CODE
command.strip!
out, err, _st = Open3.capture3(command)
warn " RuCaptcha #{err.strip}" if err.present?
out
end
# Generate a simple captcha image for Windows Platform
def convert_for_windows(size, code)
png_file_path = Rails.root.join('tmp', 'cache', "#{code}.png")
command = "convert -size #{size} xc:White -gravity Center -weight 12 -pointsize 20 -annotate 0 \"#{code}\" -trim #{png_file_path}"
_out, err, _st = Open3.capture3(command)
warn " RuCaptcha #{err.strip}" if err.present?
png_file_path
end
# Geneate a uniq rgba colors for each chars
def uniq_rgbs_for_each_chars(chars)
rgbs = []
chars.count.times do |i|
color = random_color
if i > 0
preview_color = rgbs[i - 1]
# Avoid color same as preview color
if color.index(color.min) == preview_color.index(preview_color.min) &&
color.index(color.max) == preview_color.index(preview_color.max)
# adjust RGB order
color = [color[1], color[2], color[0]]
end
end
rgbs << color
end
rgbs
end
def rand_line_top(text_top, font_size)
text_top + rand(font_size * 0.7).to_i
end
def warn(msg)
msg = " RuCaptcha #{msg}"
Rails.logger.error(msg)
end
end
end
end

View File

@ -1,20 +1,11 @@
module RuCaptcha module RuCaptcha
class Configuration class Configuration
# Image font size, default 45
attr_accessor :font_size
# Number of chars, default 4
attr_accessor :len
# implode, default 0.3
attr_accessor :implode
# Store Captcha code where, this config more like Rails config.cache_store # Store Captcha code where, this config more like Rails config.cache_store
# default: Rails application config.cache_store # default: Rails application config.cache_store
attr_accessor :cache_store attr_accessor :cache_store
# Number of Captcha codes limit
# set 0 to disable limit and file cache, default: 100
attr_accessor :cache_limit
# Color style, default: :colorful, allows: [:colorful, :black_white]
attr_accessor :style
# rucaptcha expire time, default 2 minutes # rucaptcha expire time, default 2 minutes
attr_accessor :expires_in attr_accessor :expires_in
# Color style, default: :colorful, allows: [:colorful, :black_white]
attr_accessor :style
end end
end end

View File

@ -14,13 +14,13 @@ module RuCaptcha
# Generate a new Captcha # Generate a new Captcha
def generate_rucaptcha def generate_rucaptcha
code = RuCaptcha::Captcha.random_chars res = RuCaptcha.generate()
session_val = { session_val = {
code: code, code: res[0],
time: Time.now.to_i time: Time.now.to_i
} }
RuCaptcha.cache.write(rucaptcha_sesion_key_key, session_val, expires_in: RuCaptcha.config.expires_in) RuCaptcha.cache.write(rucaptcha_sesion_key_key, session_val, expires_in: RuCaptcha.config.expires_in)
RuCaptcha::Captcha.create(code) res[1]
end end
# Verify captcha code # Verify captcha code

View File

@ -3,11 +3,6 @@ module RuCaptcha
isolate_namespace RuCaptcha isolate_namespace RuCaptcha
initializer 'rucaptcha.prepend.cache' do initializer 'rucaptcha.prepend.cache' do
# enable cache if cache_limit less than 1
if RuCaptcha.config.cache_limit >= 1
RuCaptcha::Captcha.send(:prepend, RuCaptcha::Cache)
end
cache_store = RuCaptcha.config.cache_store cache_store = RuCaptcha.config.cache_store
store_name = cache_store.is_a?(Array) ? cache_store.first : cache_store store_name = cache_store.is_a?(Array) ? cache_store.first : cache_store
if [:memory_store, :null_store, :file_store].include?(store_name) if [:memory_store, :null_store, :file_store].include?(store_name)

View File

@ -1,3 +1,3 @@
module RuCaptcha module RuCaptcha
VERSION = '1.2.0' VERSION = '2.0.0'
end end

View File

@ -5,8 +5,8 @@ module RuCaptcha
opts[:type] = 'text' opts[:type] = 'text'
opts[:autocorrect] = 'off' opts[:autocorrect] = 'off'
opts[:autocapitalize] = 'off' opts[:autocapitalize] = 'off'
opts[:pattern] = '[0-9a-zA-Z]*' opts[:pattern] = '[a-zA-Z]*'
opts[:maxlength] = RuCaptcha.config.len opts[:maxlength] = 5
opts[:autocomplete] = 'off' opts[:autocomplete] = 'off'
tag(:input, opts) tag(:input, opts)
end end

View File

@ -8,12 +8,18 @@ Gem::Specification.new do |s|
s.version = RuCaptcha::VERSION s.version = RuCaptcha::VERSION
s.authors = 'Jason Lee' s.authors = 'Jason Lee'
s.email = 'huacnlee@gmail.com' s.email = 'huacnlee@gmail.com'
s.files = Dir.glob('lib/**/*') + Dir.glob('app/**/*') + Dir.glob('config/**/*') + %w(README.md CHANGELOG.md) s.files = Dir.glob('lib/**/*.{rb}') +
Dir.glob("ext/**/*.{h,c,rb}") +
Dir.glob('app/**/*') +
Dir.glob('config/**/*') +
%w[README.md CHANGELOG.md]
s.homepage = 'https://github.com/huacnlee/rucaptcha' s.homepage = 'https://github.com/huacnlee/rucaptcha'
s.require_paths = ['lib'] s.require_paths = ['lib']
s.extensions = %w[ext/rucaptcha/extconf.rb]
s.summary = 'This is a Captcha gem for Rails Application. It run ImageMagick command to draw Captcha image.' s.summary = 'This is a Captcha gem for Rails Application. It run ImageMagick command to draw Captcha image.'
s.license = "MIT" s.license = "MIT"
s.required_ruby_version = ">= 2.0.0" s.required_ruby_version = ">= 2.0.0"
s.add_dependency 'railties', '>= 3.2' s.add_dependency 'railties', '>= 3.2'
s.add_development_dependency 'rake-compiler', '~> 1'
end end

View File

@ -1,26 +0,0 @@
require 'spec_helper'
describe RuCaptcha::Cache do
before(:all) do
RuCaptcha::Captcha.send(:prepend, RuCaptcha::Cache)
end
describe '.random_chars_with_cache' do
it 'should generate max chars by config.cache_limit' do
allow(RuCaptcha.config).to receive(:cache_limit).and_return(5)
items = []
100.times do
items << RuCaptcha::Captcha.random_chars
end
expect(items.uniq.length).to eq RuCaptcha.config.cache_limit
expect(RuCaptcha::Captcha.cached_codes).to eq items.uniq
end
end
describe '.create' do
it 'should work' do
expect(RuCaptcha::Captcha).to receive(:create).and_return('aabb')
expect(RuCaptcha::Captcha.create('abcd')).to eq('aabb')
end
end
end

View File

@ -1,64 +1,28 @@
require 'spec_helper' require 'spec_helper'
describe RuCaptcha::Captcha do describe RuCaptcha do
describe '.random_chars' do describe '.generate' do
it 'should work' do
res = RuCaptcha.generate()
expect(res.length).to eq(2)
expect(res[0].length).to eq(5)
expect(res[1]).not_to eq(nil)
end
end
describe '.create' do
it 'should len equal config.len' do it 'should len equal config.len' do
expect(RuCaptcha::Captcha.random_chars.length).to eq(RuCaptcha.config.len) res = RuCaptcha.create(0)
expect(res.length).to eq(2)
expect(res[0].length).to eq(5)
expect(res[1]).not_to eq(nil)
end end
it 'should return 0-9 and lower str' do it 'should work with color style' do
expect(RuCaptcha::Captcha.random_chars).to match(/[a-z0-9]/) res = RuCaptcha.create(1)
end expect(res.length).to eq(2)
expect(res[0].length).to eq(5)
it 'should not include [0ol1]' do expect(res[1]).not_to eq(nil)
10_000.times do
expect(RuCaptcha::Captcha.random_chars).not_to match(/[0ol1]/i)
end
end
end
describe '.random_color' do
it 'should return colorful array' do
allow(RuCaptcha.config).to receive(:style).and_return(:colorful)
colors = RuCaptcha::Captcha.random_color
expect(colors.uniq.size >= 2).to eq true
colors1 = RuCaptcha::Captcha.random_color
expect(colors).not_to eq colors1
end
it 'should return black color array' do
allow(RuCaptcha.config).to receive(:style).and_return(:black_white)
colors = RuCaptcha::Captcha.random_color
expect(colors.uniq.size).to eq 1
colors1 = RuCaptcha::Captcha.random_color
expect(colors).not_to eq colors1
end
end
describe '.rand_line_top' do
it 'should work' do
expect(RuCaptcha::Captcha.send(:rand_line_top, 1, 24)).to be_a(Integer)
end
end
describe '.uniq_rgbs_for_each_chars' do
let(:chars) { %w(a b c d e) }
let(:colors) { RuCaptcha::Captcha.send(:uniq_rgbs_for_each_chars, chars) }
it 'should work' do
expect(colors.length).to eq chars.length
expect(colors[0].length).to eq 3
end
it 'Be sure the color not same as preview color' do
pre_rgb = nil
colors.each do |rgb|
if pre_rgb
same = rgb.index(rgb.min) == pre_rgb.index(rgb.min) && rgb.index(rgb.max) == pre_rgb.index(pre_rgb.max)
expect(same).not_to eq true
end
pre_rgb = rgb
end
end end
end end
end end

View File

@ -3,9 +3,6 @@ require 'spec_helper'
describe RuCaptcha do describe RuCaptcha do
describe 'normal' do describe 'normal' do
it 'should read right config with spec_helper set' do it 'should read right config with spec_helper set' do
expect(RuCaptcha.config.len).to eq(2)
expect(RuCaptcha.config.font_size).to eq(48)
expect(RuCaptcha.config.implode).to eq(0.111)
expect(RuCaptcha.config.expires_in).to eq(2.minutes) expect(RuCaptcha.config.expires_in).to eq(2.minutes)
end end
end end

View File

@ -37,9 +37,9 @@ describe RuCaptcha do
describe '.generate_rucaptcha' do describe '.generate_rucaptcha' do
it 'should work' do it 'should work' do
expect(RuCaptcha::Captcha).to receive(:random_chars).and_return('abcd') allow(RuCaptcha).to receive(:create).and_return(['abcde', 'fake image data'])
expect(simple.generate_rucaptcha).not_to be_nil expect(simple.generate_rucaptcha).to eq 'fake image data'
expect(simple.custom_session[:code]).to eq('abcd') expect(simple.custom_session[:code]).to eq('abcde')
end end
end end

View File

@ -21,8 +21,5 @@ module Rails
end end
RuCaptcha.configure do RuCaptcha.configure do
self.len = 2
self.font_size = 48
self.implode = 0.111
self.cache_store = :mem_cache_store self.cache_store = :mem_cache_store
end end

2
test Executable file
View File

@ -0,0 +1,2 @@
rake clean && rake compile > /dev/null 2& > /dev/null
rake preview > /tmp/rucaptcha-test.gif && open /tmp/rucaptcha-test.gif