OSDN Git Service

refactoring decimal method
[mint/mint-lib.git] / lib / mint / generator / square_root_arithmetic.rb
1 # -*- coding: nil -*-
2
3 module Mint::Generator
4
5   #
6   # 無理数の四則演算を生成するジェネレータ
7   #
8   # == オプション
9   # [_term_number_]
10   #   生成する式の項数を1以上の整数で指定します。
11   # [_operators_]
12   #   使用可能な演算子を指定します。
13   #   +, - の2種類から使用したいものを配列で指定します。
14   # [_min_]
15   #   生成する式の各項の最小値を0以上の整数で指定します。
16   # [_max_]
17   #   生成する式の各項の最大値を0以上の整数で指定します。
18   #   _min_ よりも小さい値を指定することは出来ません。
19   # [_upper_limit_]
20   #    すべてが数字だった場合は強制的に1つ根号を追加します。
21   #    そのときの根号内の数字の最大値を2以上の整数で指定します。
22   #    なお、最小値は自動的に2に設定されます。
23   # [_single_term_min_]
24   #   生成する式の項数の最小値を1以上の整数で指定します。
25   # [_single_term_max_]
26   #   生成する式の項数の最大値を1以上の整数で指定します。
27   #   _single_term_min_ より小さい値は指定出来ません。 
28   # [_use_coefficient_]
29   #   真を指定すると根号を含む項の係数を生成します。
30   # [_use_power_]
31   #   真を指定すると2乗項を生成します。
32   #
33   class SquareRootArithmetic < Arithmetic
34
35 #     root_pattern = /[2-9]?sqrt\((?:[2-9]|\d\d)\)/
36 #     term_pattern = /#{root_pattern} [+\-] #{root_pattern}/
37 #     validation /\A\(#{root_pattern}\)\^2\z/
38 #     validation /\A#{term_pattern}\z/
39 #     validation /\A\(#{term_pattern}\)(?:\^2)? [+\-] \(#{term_pattern}(?:\^2)?\)\z/
40 #     validation /\A\(#{term_pattern}\) [\*\/] \(#{term_pattern}\)\z/
41 #     validation /\A#{root_pattern} \* #{root_pattern}\z/
42
43     private
44
45     option :min,             0
46     option :max,             20
47     option :upper_limit,     20
48     option :operators,       %w[ * ]
49     option :use_power,       false
50     option :use_coefficient, false
51     option :single_term_min, 2
52     option :single_term_max, 2
53
54     def operand
55       results = square_root_expression_parts
56       result = results[0..-2].join(' ')
57       result = "(#{result})" if results.size > 2
58       result = "#{result}^2" if options[:use_power] && rand(2) == 0
59       result
60     end
61
62     def swap_options_with(temp)
63       temp_options = options.dup
64       self.options = temp_options.merge(temp)
65       yield
66       self.options = temp_options
67     end
68
69     def have_common_prime_divisor?(numbers)
70       return true unless numbers.size == 2
71       return true if numbers.include?(0)
72       return true if numbers.any? {|n| n < 10 }
73       prime_divisors = numbers.map {|num| num.factorize }
74       prime_divisors[0].each do |hoge|
75         prime_divisors[1].each do |piyo|
76           commons = hoge & piyo
77           return true unless commons.empty? || commons.first == 1
78         end
79       end
80       false
81     end
82
83     def square_root_expression_parts
84       term_number = term_number(:single)
85       results = []
86       if options[:use_power]
87         begin
88           numbers = Array.new(term_number) { create_integer(options[:min], options[:max], false) }
89         end until numbers.size == 1 || have_common_prime_divisor?(numbers)
90         results = numbers.map {|number| create_square_root_number(number) }
91       else
92         results = Array.new(term_number) { create_square_root_number }
93       end
94       results.sort!
95       if results.all? {|r| /sqrt/ !~ r }
96         results.delete_at rand(results.length)
97         swap_options_with(:min => 2, :max => options[:upper_limit]) do
98           results << create_square_root_number
99         end
100       end
101       operators = term_number.times.map { %w[ + - ].sample }
102       results.zip(operators).flatten
103     end
104
105     def create_square_root_number(number = nil)
106       if number.nil?
107         number = create_integer(options[:min], options[:max], false)
108       end
109       "#{coefficient(number)}#{root(number)}"
110     end
111
112     def root(number)
113       zero_or_one?(number) ? '' : "sqrt(#{number})"
114     end
115
116     def coefficient(number)
117       if options[:use_coefficient] || zero_or_one?(number)
118         return create_integer(2, 9, false)
119       end
120       nil
121     end
122
123     def zero_or_one?(number)
124       number == 0 || number == 1
125     end
126   end
127 end
128