zakuro 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +9 -0
  3. data/.gitignore +69 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +14 -0
  6. data/.travis.yml +6 -0
  7. data/Gemfile +19 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +44 -0
  10. data/Rakefile +20 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/images/12/346/234/210/344/273/245/351/231/215/343/201/256/345/205/245/345/256/232/346/260/227.png +0 -0
  14. data/images/12/346/234/210/344/273/245/351/231/215/343/201/256/346/234/210/343/201/256/351/200/262/351/200/200.png +0 -0
  15. data/images/source/description.numbers +0 -0
  16. data/images//345/205/245/345/256/232/346/260/227/343/201/256/350/265/267/347/256/227.png +0 -0
  17. data/images//345/220/204/346/234/210/343/201/256/346/261/202/343/202/201/346/226/271.png +0 -0
  18. data/images//345/244/252/351/231/275/343/201/250/346/234/210.png +0 -0
  19. data/lib/zakuro.rb +9 -0
  20. data/lib/zakuro/condition.rb +239 -0
  21. data/lib/zakuro/cycle/abstract_remainder.rb +457 -0
  22. data/lib/zakuro/cycle/zodiac.rb +103 -0
  23. data/lib/zakuro/era/gengou/set-001-until-south.yaml +375 -0
  24. data/lib/zakuro/era/gengou/set-002-from-north.yaml +166 -0
  25. data/lib/zakuro/era/gengou/set-003-modern.yaml +12 -0
  26. data/lib/zakuro/era/japan.rb +630 -0
  27. data/lib/zakuro/era/western.rb +412 -0
  28. data/lib/zakuro/merchant.rb +57 -0
  29. data/lib/zakuro/output/error.rb +10 -0
  30. data/lib/zakuro/output/logger.rb +64 -0
  31. data/lib/zakuro/output/response.rb +170 -0
  32. data/lib/zakuro/output/result.rb +219 -0
  33. data/lib/zakuro/output/stringifier.rb +62 -0
  34. data/lib/zakuro/version.rb +7 -0
  35. data/lib/zakuro/version/abstract_version.rb +29 -0
  36. data/lib/zakuro/version/genka/genka.rb +19 -0
  37. data/lib/zakuro/version/gihou/gihou.rb +19 -0
  38. data/lib/zakuro/version/gregorio/gregorio.rb +19 -0
  39. data/lib/zakuro/version/houryaku/houryaku.rb +19 -0
  40. data/lib/zakuro/version/joukyou/joukyou.rb +19 -0
  41. data/lib/zakuro/version/kansei/kansei.rb +19 -0
  42. data/lib/zakuro/version/senmyou/README.md +586 -0
  43. data/lib/zakuro/version/senmyou/base/era.rb +81 -0
  44. data/lib/zakuro/version/senmyou/base/gengou.rb +210 -0
  45. data/lib/zakuro/version/senmyou/base/remainder.rb +60 -0
  46. data/lib/zakuro/version/senmyou/base/solar_term.rb +66 -0
  47. data/lib/zakuro/version/senmyou/base/year.rb +58 -0
  48. data/lib/zakuro/version/senmyou/monthly/lunar_phase.rb +220 -0
  49. data/lib/zakuro/version/senmyou/monthly/month.rb +112 -0
  50. data/lib/zakuro/version/senmyou/senmyou.rb +34 -0
  51. data/lib/zakuro/version/senmyou/stella/lunar_orbit.rb +332 -0
  52. data/lib/zakuro/version/senmyou/stella/solar_average.rb +192 -0
  53. data/lib/zakuro/version/senmyou/stella/solar_orbit.rb +398 -0
  54. data/lib/zakuro/version/senmyou/stella/winter_solstice.rb +106 -0
  55. data/lib/zakuro/version/senmyou/summary/annual_data.rb +186 -0
  56. data/lib/zakuro/version/senmyou/summary/gengou_data.rb +294 -0
  57. data/lib/zakuro/version/taien/taien.rb +19 -0
  58. data/lib/zakuro/version/tenpou/tenpou.rb +19 -0
  59. data/lib/zakuro/version_factory.rb +59 -0
  60. data/zakuro.gemspec +31 -0
  61. metadata +106 -0
@@ -0,0 +1,192 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nodoc:
4
+ module Zakuro
5
+ # :nodoc:
6
+ module Senmyou
7
+ #
8
+ # SolarAverage 常気(太陽軌道平均)
9
+ #
10
+ module SolarAverage
11
+ # @return [Remainder] 気策(24分の1年)
12
+ SOLAR_TERM_AVERAGE = Remainder.new(day: 15, minute: 1835, second: 5)
13
+
14
+ # :reek:TooManyStatements { max_statements: 6 }
15
+
16
+ #
17
+ # 冬至から数えた1年データの月ごとに二十四節気を割り当てる
18
+ #
19
+ # @param [Integer] western_year 西暦年
20
+ # @param [Array<Month>] annual_data 1年データ
21
+ #
22
+ # @return [Array<Month>] 1年データ
23
+ #
24
+ def self.set_solar_terms_into_annual_data(western_year:, annual_data:)
25
+ # 天正冬至
26
+ winter_solstice = WinterSolstice.calc(western_year: western_year)
27
+
28
+ # 前年冬至からの二十四節気
29
+ solar_terms = collect_solar_terms_from_last_winter_solstice(
30
+ winter_solstice: winter_solstice
31
+ )
32
+
33
+ apply_solar_terms_from_last_winter_solstice(annual_data: annual_data,
34
+ solar_terms: solar_terms)
35
+
36
+ # 前後の二十四節気
37
+ rest_solar_terms = \
38
+ collect_solar_terms_before_and_after(solar_terms: solar_terms)
39
+
40
+ apply_solar_terms_before_and_after(annual_data: annual_data,
41
+ rest_solar_terms: rest_solar_terms)
42
+
43
+ annual_data
44
+ end
45
+
46
+ # :reek:TooManyStatements { max_statements: 6 }
47
+
48
+ #
49
+ # 前年冬至から当年大雪までの二十四節気を計算する
50
+ #
51
+ # @param [Remainder] winter_solstice 冬至
52
+ #
53
+ # @return [Array<Remainder>] 二十四節気
54
+ #
55
+ def self.collect_solar_terms_from_last_winter_solstice(winter_solstice:)
56
+ result = []
57
+ term = winter_solstice
58
+ (0...24).each do |_i|
59
+ result.push(term)
60
+ term = term.add(SOLAR_TERM_AVERAGE)
61
+ end
62
+
63
+ result
64
+ end
65
+ private_class_method :collect_solar_terms_from_last_winter_solstice
66
+
67
+ # :reek:TooManyStatements { max_statements: 9 }
68
+
69
+ #
70
+ # 各月の二十四節気を設定する
71
+ #
72
+ # @param [Array<Month>] annual_data 1年データ
73
+ # @param [Array<Remainder>] solar_terms 1年データ内の全二十四節気
74
+ #
75
+ def self.apply_solar_terms_from_last_winter_solstice(annual_data:,
76
+ solar_terms:)
77
+ c_idx = 0
78
+ st_idx = 0
79
+ while c_idx < annual_data.size && st_idx < solar_terms.size
80
+ current_month = annual_data[c_idx]
81
+ next_month = annual_data[c_idx + 1]
82
+ solar_term = solar_terms[st_idx]
83
+
84
+ if in_range_solar_term?(target: solar_term, min: current_month.remainder,
85
+ max: next_month.remainder)
86
+ set_solar_term(month: current_month,
87
+ solar_term: solar_term, solar_term_index: st_idx)
88
+ st_idx += 1
89
+ next
90
+ end
91
+ c_idx += 1
92
+ end
93
+ end
94
+ private_class_method :apply_solar_terms_from_last_winter_solstice
95
+
96
+ #
97
+ # 1年データ内の二十四節気の前後を収集する
98
+ #
99
+ # @param [Array<Remainder>] solar_terms 1年データ内の全二十四節気
100
+ #
101
+ # @return [Hash<Integer, Hash<Symbol, Integer>>, Hash<Integer, Hash<Symbol, Remainder>>] 前後
102
+ #
103
+ def self.collect_solar_terms_before_and_after(solar_terms:)
104
+ raise ArgumentError, 'parameter must be 24 solar terms' unless solar_terms.size == 24
105
+
106
+ touji = solar_terms[-1].add(SOLAR_TERM_AVERAGE)
107
+ {
108
+ # 前年大雪
109
+ 23 => { index: 0, solar_term: solar_terms[0].sub(SOLAR_TERM_AVERAGE) },
110
+ # 当年冬至
111
+ 0 => { index: -2, solar_term: touji },
112
+ # 当年小寒
113
+ 1 => { index: -2, solar_term: touji.add(SOLAR_TERM_AVERAGE) }
114
+ }
115
+ end
116
+ private_class_method :collect_solar_terms_before_and_after
117
+
118
+ # :reek:TooManyStatements { max_statements: 6 }
119
+
120
+ #
121
+ # 1年データ前後の二十四節気を適用する
122
+ #
123
+ # @param [Array<Month>] annual_data 1年データ
124
+ # @param [Hash<Integer, Hash<Symbol, Integer>>, Hash<Integer, Hash<Symbol, Remainder>>]
125
+ # rest_solar_terms 前後
126
+ #
127
+ def self.apply_solar_terms_before_and_after(annual_data:, rest_solar_terms:)
128
+ rest_solar_terms.each do |key, value|
129
+ index = value[:index]
130
+ solar_term = value[:solar_term]
131
+ data = annual_data[index]
132
+ next unless in_range_solar_term?(
133
+ target: solar_term,
134
+ min: data.remainder, max: annual_data[index + 1].remainder
135
+ )
136
+
137
+ set_solar_term(month: data,
138
+ solar_term: solar_term, solar_term_index: key)
139
+ end
140
+ end
141
+ private_class_method :apply_solar_terms_before_and_after
142
+
143
+ # :reek:TooManyStatements { max_statements: 7 }
144
+
145
+ #
146
+ # 月内(currentからnextの間)に二十四節気があるか
147
+ # @note 大余60で一巡するため 以下2パターンがある
148
+ # * current(min) <= next(max) : (二十四節気) >= current && (二十四節気) < next
149
+ # * current(min) > next(max) : (二十四節気) >= current || (二十四節気) < next
150
+ #
151
+ # @param [Remainder] target 対象の二十四節気
152
+ # @param [Remainder] min 月初
153
+ # @param [Remainder] max 月末
154
+ #
155
+ # @return [True] 対象の二十四節気がある
156
+ # @return [False] 対象の二十四節気がない
157
+ #
158
+ def self.in_range_solar_term?(target:, min:, max:)
159
+ # 大余で比較する
160
+ target_time = target.day
161
+ min_time = min.day
162
+ max_time = max.day
163
+ min_over = (target_time >= min_time)
164
+ max_under = (target_time < max_time)
165
+
166
+ (min_time <= max_time ? min_over && max_under : min_over || max_under)
167
+ end
168
+ private_class_method :in_range_solar_term?
169
+
170
+ #
171
+ # 二十四節気を設定する
172
+ #
173
+ # @param [Month] month 月
174
+ # @param [Remainder] solar_term 二十四節気
175
+ # @param [Integer] solar_term_index 連番(二十四節気)
176
+ #
177
+ def self.set_solar_term(month:, solar_term:, solar_term_index:)
178
+ term = SolarTerm.new(remainder: solar_term, index: solar_term_index)
179
+ if solar_term_index.even?
180
+ # 中気
181
+ month.even_term = term
182
+ else
183
+ # 節気
184
+ month.odd_term = term
185
+ end
186
+
187
+ nil
188
+ end
189
+ private_class_method :set_solar_term
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,398 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../base/solar_term'
4
+
5
+ # :nodoc:
6
+ module Zakuro
7
+ # :nodoc:
8
+ module Senmyou
9
+ #
10
+ # SolarOrbit 太陽軌道
11
+ #
12
+ module SolarOrbit
13
+ # @return [Integer] 統法(1日=8400分)
14
+ DAY = 8400
15
+
16
+ #
17
+ # Interval 入気定日加減数(二十四節気の間隔)
18
+ #
19
+ module Interval
20
+ # @return [Hash<Symbol, Remainder>] 一覧
21
+ LIST = {
22
+ # 冬至(とうじ)・大雪(たいせつ)
23
+ touji: Remainder.new(day: 14, minute: 4235, second: 5),
24
+ taisetsu: Remainder.new(day: 14, minute: 4235, second: 5),
25
+ # 小寒(しょうかん)・小雪(しょうせつ)
26
+ shoukan: Remainder.new(day: 14, minute: 5235, second: 5),
27
+ shousetsu: Remainder.new(day: 14, minute: 5235, second: 5),
28
+ # 大寒(だいかん)・立冬(りっとう)
29
+ daikan: Remainder.new(day: 14, minute: 6235, second: 5),
30
+ rittou: Remainder.new(day: 14, minute: 6235, second: 5),
31
+ # 立春(りっしゅん)・霜降(そうこう)
32
+ risshun: Remainder.new(day: 14, minute: 7235, second: 5),
33
+ soukou: Remainder.new(day: 14, minute: 7235, second: 5),
34
+ # 雨水(うすい)・寒露(かんろ)
35
+ usui: Remainder.new(day: 15, minute: 35, second: 5),
36
+ kanro: Remainder.new(day: 15, minute: 35, second: 5),
37
+ # 啓蟄(けいちつ)・秋分(しゅうぶん)
38
+ keichitsu: Remainder.new(day: 15, minute: 1235, second: 5),
39
+ shuubun: Remainder.new(day: 15, minute: 1235, second: 5),
40
+ # 春分(しゅんぶん)・白露(はくろ)
41
+ shunbun: Remainder.new(day: 15, minute: 2435, second: 5),
42
+ hakuro: Remainder.new(day: 15, minute: 2435, second: 5),
43
+ # 清明(せいめい)・処暑(しょしょ)
44
+ seimei: Remainder.new(day: 15, minute: 3635, second: 5),
45
+ shosho: Remainder.new(day: 15, minute: 3635, second: 5),
46
+ # 穀雨(こくう)・立秋(りっしゅう)
47
+ kokuu: Remainder.new(day: 15, minute: 4835, second: 5),
48
+ risshuu: Remainder.new(day: 15, minute: 4835, second: 5),
49
+ # 立夏(りっか)・大暑(たいしょ)
50
+ rikka: Remainder.new(day: 15, minute: 5835, second: 5),
51
+ taisho: Remainder.new(day: 15, minute: 5835, second: 5),
52
+ # 小満(しょうまん)・小暑(しょうしょ)
53
+ shouman: Remainder.new(day: 15, minute: 6835, second: 5),
54
+ shousho: Remainder.new(day: 15, minute: 6835, second: 5),
55
+ # 芒種(ぼうしゅ)・夏至(げし)
56
+ boushu: Remainder.new(day: 15, minute: 7835, second: 5),
57
+ geshi: Remainder.new(day: 15, minute: 7835, second: 5)
58
+ }.freeze
59
+
60
+ # @return [Array<Remainder>] 索引
61
+ INDEXES = [
62
+ LIST[:touji], # 0
63
+ LIST[:shoukan], # 1
64
+ LIST[:daikan], # 2
65
+ LIST[:risshun], # 3
66
+ LIST[:usui], # 4
67
+ LIST[:keichitsu], # 5
68
+ LIST[:shunbun], # 6
69
+ LIST[:seimei], # 7
70
+ LIST[:kokuu], # 8
71
+ LIST[:rikka], # 9
72
+ LIST[:shouman], # 10
73
+ LIST[:boushu], # 11
74
+ LIST[:geshi], # 12
75
+ LIST[:shousho], # 13
76
+ LIST[:taisho], # 14
77
+ LIST[:risshuu], # 15
78
+ LIST[:shosho], # 16
79
+ LIST[:hakuro], # 17
80
+ LIST[:shuubun], # 18
81
+ LIST[:kanro], # 19
82
+ LIST[:soukou], # 20
83
+ LIST[:rittou], # 21
84
+ LIST[:shousetsu], # 22
85
+ LIST[:taisetsu] # 23
86
+ ].freeze
87
+ end
88
+
89
+ #
90
+ # 24気損益眺朒(ちょうじく)数
91
+ #
92
+ module Adjustment
93
+ #
94
+ # Item 24気損益眺朒(ちょうじく)数
95
+ #
96
+ class Item
97
+ # @return [Integer] 眺朒(ちょうじく)積
98
+ attr_reader :stack
99
+ # @return [Integer] 眺朒(ちょうじく)数
100
+ attr_reader :per_term
101
+ # @return [Integer] 毎日差
102
+ attr_reader :per_day
103
+
104
+ #
105
+ # 初期化
106
+ #
107
+ # @param [Integer] stack 眺朒(ちょうじく)積
108
+ # @param [Integer] per_term 眺朒(ちょうじく)数
109
+ # @param [Integer] per_day 毎日差
110
+ #
111
+ def initialize(stack:, per_term:, per_day:)
112
+ @stack = stack
113
+ @per_term = per_term
114
+ @per_day = per_day
115
+ end
116
+
117
+ #
118
+ # 文字化
119
+ #
120
+ # @return [String] 文字
121
+ #
122
+ def to_s
123
+ "stack:#{@stack}, per_term:#{@per_term}, per_day:#{@per_day}"
124
+ end
125
+ end
126
+
127
+ # @return [Array<Item>] 24気損益眺朒(ちょうじく)数
128
+ LIST = {
129
+ touji: Item.new(stack: 0.0, per_term: +33.4511, per_day: -0.3695), # 冬至(とうじ)
130
+ shoukan: Item.new(stack: +449.0, per_term: +28.0389, per_day: -0.3606), # 小寒(しょうかん)
131
+ daikan: Item.new(stack: +823.0, per_term: +22.6998, per_day: -0.3519), # 大寒(だいかん)
132
+ risshun: Item.new(stack: +1122.0, per_term: +17.8923, per_day: -0.4068), # 立春(りっしゅん)
133
+ usui: Item.new(stack: +1346.0, per_term: +11.7966, per_day: -0.3998), # 雨水(うすい)
134
+ keichitsu: Item.new(stack: +1481.0, per_term: +5.7986, per_day: -0.3998), # 啓蟄(けいちつ)
135
+ shunbun: Item.new(stack: +1526.0, per_term: -0.2433, per_day: -0.3779), # 春分(しゅんぶん)
136
+ seimei: Item.new(stack: +1481.0, per_term: -6.1254, per_day: -0.3634), # 清明(せいめい)
137
+ kokuu: Item.new(stack: +1346.0, per_term: -12.2048, per_day: -0.2987), # 穀雨(こくう)
138
+ rikka: Item.new(stack: +1122.0, per_term: -16.9060, per_day: -0.2919), # 立夏(りっか)
139
+ shouman: Item.new(stack: +823.0, per_term: -21.5362, per_day: -0.2854), # 小満(しょうまん)
140
+ boushu: Item.new(stack: +449.0, per_term: -26.0498, per_day: -0.2854), # 芒種(ぼうしゅ)
141
+ geshi: Item.new(stack: 0.0, per_term: -30.3119, per_day: +0.2854), # 夏至(げし)
142
+ shousho: Item.new(stack: -449.0, per_term: -25.8126, per_day: +0.2919), # 小暑(しょうしょ)
143
+ taisho: Item.new(stack: -823.0, per_term: -21.2454, per_day: +0.2987), # 大暑(たいしょ)
144
+ risshuu: Item.new(stack: -1122.0, per_term: -17.0296, per_day: +0.3634), # 立秋(りっしゅう)
145
+ shosho: Item.new(stack: -1346.0, per_term: -11.4744, per_day: +0.3779), # 処暑(しょしょ)
146
+ hakuro: Item.new(stack: -1481.0, per_term: -5.6429, per_day: +0.3779), # 白露(はくろ)
147
+ shuubun: Item.new(stack: -1526.0, per_term: +0.1432, per_day: +0.3998), # 秋分(しゅうぶん)
148
+ kanro: Item.new(stack: -1481.0, per_term: +6.1488, per_day: +0.4068), # 寒露(かんろ)
149
+ soukou: Item.new(stack: -1346.0, per_term: +12.6336, per_day: +0.3519), # 霜降(そうこう)
150
+ rittou: Item.new(stack: -1122.0, per_term: +17.8043, per_day: +0.3606), # 立冬(りっとう)
151
+ shousetsu: Item.new(stack: -823.0, per_term: +23.0590, per_day: +0.3695), # 小雪(しょうせつ)
152
+ taisetsu: Item.new(stack: -449.0, per_term: +28.4618, per_day: +0.3695) # 大雪(たいせつ)
153
+ }.freeze
154
+ end
155
+
156
+ #
157
+ # 二十四節気 と 補正値 を計算する
158
+ #
159
+ # @param [SolarTerm] solar_term 二十四節気
160
+ #
161
+ # @return [SolarTerm] 二十四節気
162
+ # @return [Integer] 補正値
163
+ #
164
+ def self.calc_term_and_orbit_value(solar_term:)
165
+ remainder = calc_solar_term_by_remainder(
166
+ solar_term: solar_term
167
+ )
168
+ value = calc_sun_orbit_value(
169
+ solar_term: remainder
170
+ )
171
+ [remainder, value]
172
+ end
173
+
174
+ # :reek:ControlParameter and :reek:BooleanParameter
175
+
176
+ #
177
+ # 二十四節気を計算する
178
+ #
179
+ # @param [SolarTerm] solar_term 二十四節気
180
+ #
181
+ # @return [SolarTerm] 二十四節気
182
+ #
183
+ def self.calc_solar_term_by_remainder(solar_term:)
184
+ if solar_term.index.negative?
185
+ return calc_first_solar_term(
186
+ winter_solstice_age: solar_term.remainder
187
+ )
188
+ end
189
+
190
+ calc_next_solar_term_recursively(
191
+ solar_term: solar_term
192
+ )
193
+ end
194
+
195
+ # :reek:TooManyStatements { max_statements: 8 }
196
+
197
+ #
198
+ # 入定気(定気の開始点からの日時)と属する定気を計算する
199
+ #
200
+ # @param [Remainder] winter_solstice_age 天正冬至
201
+ #
202
+ # @return [SolarTerm] 二十四節気
203
+ # SolarTerm.remainder : 入定気(定気の開始点からの日時)
204
+ # SolarTerm.index : 定気
205
+ #
206
+ def self.calc_first_solar_term(winter_solstice_age:)
207
+ # 入定気の起算方法
208
+ # 概要:
209
+ # * 太陽の運行による補正値は、二十四節気の気ごとに定められる
210
+ # * 11月経朔の前にある気を求め、それから11月経朔との間隔を求める
211
+ # * 気ごとの補正値と、気から11月経朔までにかかる補正値を求める
212
+ # 前提:
213
+ # * 11月経朔に関わる二十四節気は、時系列から順に、小雪・大雪・冬至である
214
+ # * 小雪〜大雪の間隔は小雪定数で、大雪〜冬至の間隔は大雪定数で決められている(24気損益眺朒(ちょうじく)数のこと)
215
+ # * 11月経朔は、この小雪〜冬至の間のいずれかにある
216
+ # 計算:
217
+ # 2パターンある
218
+ # (a) 大雪〜冬至にある場合
219
+ # *「大雪定数 >= 天正閏余」の場合を指す
220
+ # * * NOTE 資料では「より大きい(>)」とされるが、大雪そのものの場合は大雪から起算すべき
221
+ # * この場合は、大雪〜経朔の間隔を求める
222
+ # (b) 小雪〜大雪にある場合
223
+ # *「大雪定数 < 天正閏余」の場合を指す
224
+ # * この場合は、小雪〜経朔の間隔を求める
225
+ taisetsu = 23
226
+ taisetsu_interval = Interval::INDEXES[taisetsu]
227
+
228
+ if winter_solstice_age > taisetsu_interval
229
+ # (b)
230
+ shousetsu = 22
231
+ diff = winter_solstice_age.sub(taisetsu_interval)
232
+
233
+ return SolarTerm.new(remainder: Interval::INDEXES[shousetsu].sub(diff),
234
+ index: shousetsu)
235
+ end
236
+ # (a)
237
+ SolarTerm.new(remainder: taisetsu_interval.sub(winter_solstice_age),
238
+ index: taisetsu)
239
+ end
240
+
241
+ # :reek:TooManyStatements { max_statements: 8 }
242
+
243
+ #
244
+ # 次の二十四節気を計算する
245
+ #
246
+ # @param [SolarTerm] solar_term 今回の二十四節気
247
+ #
248
+ # @return [SolarTerm] 次回の二十四節気
249
+ #
250
+ def self.calc_next_solar_term_recursively(solar_term:)
251
+ remainder = solar_term.remainder
252
+ index = solar_term.index
253
+ interval = Interval::INDEXES[index]
254
+ return solar_term if remainder < interval
255
+
256
+ remainder.sub!(interval)
257
+ index += 1
258
+ index = 0 if index >= Interval::INDEXES.size
259
+ calc_next_solar_term_recursively(
260
+ solar_term: SolarTerm.new(remainder: remainder, index: index)
261
+ )
262
+ end
263
+
264
+ #
265
+ # 太陽の運行による補正値を算出する
266
+ #
267
+ # @param [SolarTerm] solar_term 二十四節気
268
+ #
269
+ # @return [Integer] 補正値
270
+ #
271
+ def self.calc_sun_orbit_value(solar_term:)
272
+ remainder = solar_term.remainder
273
+
274
+ adjustment = specify_solar_term_adjustment(index: solar_term.index)
275
+ # 損益率/眺朒(ちょうじく)数
276
+ # パラメータ:
277
+ # a: 眺朒(ちょうじく)数の初日の値
278
+ # b: 損益率初日の値
279
+ # c: 損益率の毎日の差
280
+ # n: 定気の日から数えた日数
281
+
282
+ day_stack = calc_day_stack(remainder: remainder, adjustment: adjustment)
283
+
284
+ month_stack = calc_month_stack(stack: adjustment.stack, day: remainder.day,
285
+ per_term: adjustment.per_term, per_day:
286
+ adjustment.per_day)
287
+
288
+ # 冬至であれば眺朒数がプラスになり続けて損益率が「益」で、小雪であればマイナスの眺朒数がプラスされ続けて「損」
289
+ month_stack + day_stack
290
+ end
291
+
292
+ #
293
+ # 損益率を求める
294
+ #
295
+ # @param [Remainder] remainder 入定気
296
+ # @param [Adjustment::Item] adjustment 24気損益眺朒(ちょうじく)数
297
+ #
298
+ # @return [Integer] 損益率
299
+ #
300
+ def self.calc_day_stack(remainder:, adjustment:)
301
+ per_term = adjustment.per_term
302
+ per_day = adjustment.per_day
303
+ sign, ratio = calc_ratio(day: remainder.day, per_term: per_term, per_day: per_day)
304
+
305
+ day_stack = calc_day_stack_from_ratio(sign: sign, ratio: ratio,
306
+ minute: remainder.minute)
307
+
308
+ day_stack
309
+ end
310
+ private_class_method :calc_day_stack
311
+
312
+ #
313
+ # 24気損益眺朒(ちょうじく)数を特定する
314
+ #
315
+ # @param [Integer] index 連番(二十四節気)
316
+ #
317
+ # @return [Adjustment::Item] 24気損益眺朒(ちょうじく)数
318
+ #
319
+ def self.specify_solar_term_adjustment(index:)
320
+ key = SolarTerm::ORDER[index]
321
+ Adjustment::LIST[key].clone
322
+ end
323
+ private_class_method :specify_solar_term_adjustment
324
+
325
+ # :reek:TooManyStatements { max_statements: 6 }
326
+
327
+ #
328
+ # 大余に対応する損益率を求める
329
+ # 損益率 = b + n * c
330
+ #
331
+ # @param [Integer] day 大余
332
+ # @param [Integer] per_term 眺朒(ちょうじく)数
333
+ # @param [Integer] per_day 毎日差
334
+ #
335
+ # @return [Integer] 正負
336
+ # @return [Integer] 大余に対応する損益率
337
+ #
338
+ def self.calc_ratio(day:, per_term:, per_day:)
339
+ ratio = per_term + day * per_day
340
+ sign = 1
341
+ if ratio.negative?
342
+ sign = -1
343
+ ratio *= sign
344
+ end
345
+ # 小数点以下は無視する
346
+ ratio = ratio.floor
347
+
348
+ [sign, ratio]
349
+ end
350
+ private_class_method :calc_ratio
351
+
352
+ #
353
+ # 小余を含めた損益率を求める
354
+ #
355
+ # @param [Integer] sign 正負(大余に対応する損益率)
356
+ # @param [Integer] ratio 大余に対応する損益率
357
+ # @param [Integer] minute 小余
358
+ #
359
+ # @return [Integer] 小余を含めた損益率
360
+ #
361
+ def self.calc_day_stack_from_ratio(sign:, ratio:, minute:)
362
+ minute_stack = ratio * minute
363
+ day_stack = (minute_stack / DAY).floor
364
+ # 四捨五入
365
+ # NOTE 資料では「この余りが4200をこえていれば切り上げる」とあり「>=」とした
366
+ # 1612年の7月(慶長17年7月)が境界値4200だが、繰り上げを行なっていたため
367
+ day_stack += 1 if minute_stack % DAY >= (DAY / 2)
368
+ day_stack *= sign
369
+
370
+ day_stack
371
+ end
372
+ private_class_method :calc_day_stack_from_ratio
373
+
374
+ # :reek:LongParameterList { max_params: 4 }
375
+
376
+ #
377
+ # 眺朒(ちょうじく)数を求める
378
+ # 眺朒(ちょうじく)数 = a + (n * b) + (1/2)n(n-1)c
379
+ #
380
+ # @param [Integer] stack 眺朒(ちょうじく)積
381
+ # @param [Integer] day 大余
382
+ # @param [Integer] per_term 眺朒(ちょうじく)数
383
+ # @param [Integer] per_day 毎日差f
384
+ #
385
+ # @return [Integer] 眺朒(ちょうじく)数
386
+ #
387
+ def self.calc_month_stack(stack:, day:, per_term:, per_day:)
388
+ month_stack = stack + day * per_term + \
389
+ (1 / 2.0) * (day * (day - 1) * per_day)
390
+ # 切り捨て(プラスマイナスに関わらず小数点以下切り捨て)
391
+ month_stack = month_stack.negative? ? month_stack.ceil : month_stack.floor
392
+
393
+ month_stack
394
+ end
395
+ private_class_method :calc_month_stack
396
+ end
397
+ end
398
+ end