zakuro 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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,220 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../output/logger'
4
+
5
+ require_relative '../stella/winter_solstice'
6
+
7
+ # :nodoc:
8
+ module Zakuro
9
+ # :nodoc:
10
+ module Senmyou
11
+ # :reek:TooManyStatements { max_statements: 7 }
12
+ # :reek:TooManyInstanceVariables { max_instance_variables: 7 }
13
+
14
+ #
15
+ # LunarPhase 月の位相
16
+ #
17
+ class LunarPhase
18
+ #
19
+ # QuarterMoon 弦
20
+ #
21
+ module QuarterMoon
22
+ # @return [Remainder] 弦(1分=8秒)
23
+ DEFAULT = Remainder.new(day: 7, minute: 3214, second: 2)
24
+ # @return [Remainder] 弦(1分=100秒)
25
+ BASE_HUNDRED = LunarRemainder.new(day: 7, minute: 3214, second: 25)
26
+ end
27
+
28
+ # @return [Logger] ロガー
29
+ LOGGER = Logger.new(location: 'lunar_phase')
30
+
31
+ # @return [Array<String>] 月内の弦
32
+ PHASE_INDEXES = %w[朔日 上弦 望月 下弦].freeze
33
+
34
+ # @return [Integer] 西暦年
35
+ attr_reader :western_year
36
+ # @return [Remainder]] 経
37
+ attr_reader :average_remainder
38
+ # @return [True] 初回計算
39
+ # @return [False] 次回以降計算
40
+ attr_reader :first
41
+ # @return [SolarTerm] 二十四節気(入定気)
42
+ attr_reader :solar_term
43
+ # @return [Remainder] 入暦
44
+ attr_reader :moon_remainder
45
+ # @return [True] 進(遠地点より数える)
46
+ # @return [False] 退(近地点より数える)
47
+ attr_reader :is_forward
48
+ # @return [Integer] 弦
49
+ attr_reader :phase_index
50
+
51
+ #
52
+ # 初期化
53
+ #
54
+ # @param [Integer] western_year 西暦年
55
+ #
56
+ def initialize(western_year:)
57
+ @western_year = western_year
58
+ # 経
59
+ @average_remainder = WinterSolstice.calc_averaged_last_november_1st(
60
+ western_year: @western_year
61
+ )
62
+ # 天正冬至
63
+ winter_solstice_age = \
64
+ WinterSolstice.calc_moon_age(western_year: @western_year)
65
+ # 入定気
66
+ @solar_term = SolarTerm.new(remainder: winter_solstice_age)
67
+ # 入暦
68
+ @moon_remainder = LunarRemainder.new(total: 0).add!(winter_solstice_age)
69
+
70
+ @is_forward = false
71
+ @first = true
72
+
73
+ # 弦
74
+ @phase_index = 0
75
+ end
76
+
77
+ #
78
+ # 次の弦に進める
79
+ #
80
+ # @return [Remainder] 定朔
81
+ #
82
+ def next_phase
83
+ adjusted = current_remainder
84
+
85
+ @first = false if @first
86
+
87
+ add_quarter_moon_size
88
+
89
+ adjusted
90
+ end
91
+
92
+ #
93
+ # 次の月に進める
94
+ # @note 進めた後の月の定朔ではなく、当月のものを返却する
95
+ #
96
+ # @return [Remainder] 当月初の定朔
97
+ #
98
+ def next_month
99
+ result = nil
100
+ PHASE_INDEXES.each_with_index do |_phase, index|
101
+ adjust = next_phase
102
+ result = adjust if index.zero?
103
+ end
104
+
105
+ result
106
+ end
107
+
108
+ private
109
+
110
+ #
111
+ # 次の弦に進める
112
+ #
113
+ # @return [Integer] 弦
114
+ #
115
+ def next_phase_index
116
+ @phase_index += 1
117
+ @phase_index = 0 if @phase_index >= PHASE_INDEXES.size
118
+ @phase_index
119
+ end
120
+
121
+ #
122
+ # 朔月(月初)かを確認する
123
+ #
124
+ # @return [True] 朔月である
125
+ # @return [False] 朔月ではない
126
+ #
127
+ def first_phase?
128
+ @phase_index.zero?
129
+ end
130
+
131
+ #
132
+ # 朔月のみログ出力する
133
+ #
134
+ # @param [String] messages メッセージ(可変長)
135
+ #
136
+ def debug(*messages)
137
+ return unless first_phase?
138
+
139
+ LOGGER.debug(*messages)
140
+ end
141
+
142
+ #
143
+ # 現在の定朔を取得する
144
+ #
145
+ # @return [Remainder] 定朔
146
+ #
147
+ def current_remainder
148
+ debug('@average_remainder.format:' + @average_remainder.format)
149
+
150
+ sum = correction_value
151
+ adjusted = @average_remainder.add(
152
+ Remainder.new(day: 0, minute: sum, second: 0)
153
+ )
154
+ adjusted.up_on_new_moon!
155
+
156
+ debug("result: #{adjusted.format}")
157
+
158
+ adjusted
159
+ end
160
+
161
+ #
162
+ # 補正値を得る
163
+ #
164
+ # @return [Integer] 補正値
165
+ #
166
+ def correction_value
167
+ sun = correction_solar_value
168
+ moon = correction_moon_value
169
+
170
+ sum = sun + moon
171
+
172
+ debug("sun: #{sun}", "moon: #{moon}", "sun + moon : #{sum}")
173
+
174
+ sum
175
+ end
176
+
177
+ #
178
+ # 太陽運動の補正値を得る
179
+ #
180
+ # @return [Integer] 太陽運動の補正値
181
+ #
182
+ def correction_solar_value
183
+ @solar_term = SolarOrbit.calc_solar_term_by_remainder(
184
+ solar_term: @solar_term
185
+ )
186
+ debug("@solar_term.remainder: #{@solar_term.remainder.format(form: '%d-%d.%d')}")
187
+ debug("@solar_term.index: #{@solar_term.index}")
188
+
189
+ SolarOrbit.calc_sun_orbit_value(solar_term: @solar_term)
190
+ end
191
+
192
+ #
193
+ # 月運動の補正値を得る
194
+ #
195
+ # @return [Integer] 月運動の補正値
196
+ #
197
+ def correction_moon_value
198
+ @moon_remainder, @is_forward = \
199
+ LunarOrbit.calc_moon_point(remainder: @moon_remainder,
200
+ western_year: @western_year,
201
+ is_forward: @is_forward,
202
+ first: @first)
203
+
204
+ debug("@moon_remainder.format: #{@moon_remainder.format}")
205
+ debug("@is_forward: #{@is_forward}")
206
+
207
+ LunarOrbit.calc_moon_orbit_value(remainder_month: @moon_remainder,
208
+ is_forward: @is_forward)
209
+ end
210
+
211
+ def add_quarter_moon_size
212
+ @average_remainder.add!(QuarterMoon::DEFAULT)
213
+ @solar_term.remainder.add!(QuarterMoon::DEFAULT)
214
+ @moon_remainder.add!(QuarterMoon::BASE_HUNDRED)
215
+
216
+ next_phase_index
217
+ end
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nodoc:
4
+ module Zakuro
5
+ # :nodoc:
6
+ module Senmyou
7
+ #
8
+ # Month 月情報
9
+ #
10
+ class Month
11
+ # @return [True] 昨年の月
12
+ # @return [False] 今年の月
13
+ # @note 冬至基準で1年データを作成するため昨年の11月-12月である可能性がある
14
+ attr_accessor :is_last_year
15
+ # @return [True] 大の月(30日)
16
+ # @return [False] 小の月(29日)
17
+ attr_accessor :is_many_days
18
+ # @return [Integer] 月(xx月のxx)
19
+ attr_accessor :number
20
+ # @return [True] 閏月
21
+ # @return [False] 平月
22
+ attr_accessor :leaped
23
+ # @return [SolarTerm] 二十四節気(中気)
24
+ attr_accessor :even_term
25
+ # @return [SolarTerm] 二十四節気(節気)
26
+ attr_accessor :odd_term
27
+ # @return [Remainder] 月初日の大余小余
28
+ attr_reader :remainder
29
+ # @return [String] 月齢(朔月、上弦、望月、下弦)
30
+ attr_reader :phase_index
31
+
32
+ # rubocop:disable Metrics/ParameterLists
33
+ # :reek:BooleanParametere
34
+
35
+ #
36
+ # 初期化
37
+ #
38
+ # @param [True, False] is_last_year 昨年の月/今年の月
39
+ # @param [Integer] number 月(xx月のxx)
40
+ # @param [True, False] is_many_days 大の月(30日)/小の月(29日)
41
+ # @param [True, False] leaped 閏月/平月
42
+ # @param [Remainder] remainder 月初日の大余小余
43
+ # @param [String] phase_index 月齢(朔月、上弦、望月、下弦)
44
+ # @param [SolarTerm] even_term 二十四節気(中気)
45
+ # @param [SolarTerm] odd_term 二十四節気(節気)
46
+ #
47
+ def initialize(is_last_year: -1, number: -1, is_many_days: false,
48
+ leaped: false, remainder: Remainder.new, phase_index: -1,
49
+ even_term: SolarTerm.new, odd_term: SolarTerm.new)
50
+ # 年
51
+ @is_last_year = is_last_year
52
+ # 月の大小
53
+ @is_many_days = is_many_days
54
+ # 月
55
+ @number = number
56
+ # 閏
57
+ @leaped = leaped
58
+ # 日
59
+ @remainder = remainder
60
+ # 月齢(朔月、上弦、望月、下弦)
61
+ @phase_index = phase_index
62
+ # 中気(二十四節気)
63
+ @even_term = even_term
64
+ # 節気(二十四節気)
65
+ @odd_term = odd_term
66
+ end
67
+ # rubocop:enable Metrics/ParameterLists
68
+
69
+ #
70
+ # 月の日数を返す
71
+ #
72
+ # @return [Integer] 日数
73
+ #
74
+ def days
75
+ @is_many_days ? 30 : 29
76
+ end
77
+
78
+ #
79
+ # 月の名前(大小)を返す
80
+ #
81
+ # @return [String] 月の名前(大小)
82
+ #
83
+ def days_name
84
+ @is_many_days ? '大' : '小'
85
+ end
86
+
87
+ #
88
+ # 同一の月情報かを検証する
89
+ #
90
+ # @param [Month] other 他の月情報
91
+ #
92
+ # @return [True] 同一の月
93
+ # @return [False] 異なる月
94
+ #
95
+ def same?(other:)
96
+ @number == other.number && @leaped == other.leaped
97
+ end
98
+
99
+ #
100
+ # 文字化する
101
+ #
102
+ # @return [String] 文字
103
+ #
104
+ def to_s
105
+ "is_last_year: #{@is_last_year}, number: #{@number}, leaped: #{@leaped}, " \
106
+ "remainder: #{@remainder.format}, phase_index: #{@phase_index}, " \
107
+ "even_term: #{@even_term.remainder.format}: #{@even_term.index}, " \
108
+ "odd_term: #{@odd_term.remainder.format}: #{@odd_term.index}"
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+ require_relative '../abstract_version'
5
+ require_relative '../../era/western'
6
+ require_relative 'summary/gengou_data'
7
+
8
+ # :nodoc:
9
+ module Zakuro
10
+ #
11
+ # Senmyou 宣明暦
12
+ #
13
+ module Senmyou
14
+ #
15
+ # Gateway アクセサメソッド群
16
+ #
17
+ class Gateway < AbstractVersion
18
+ # @return [True] リリースあり
19
+ RELEASE = true
20
+
21
+ #
22
+ # 西暦日から和暦日に変換する
23
+ #
24
+ # @param [Date] western_date 西暦日
25
+ #
26
+ # @return [Result::SingleDay] 和暦日
27
+ #
28
+ def self.to_japan_date(western_date:)
29
+ date = Western::Calendar.create(date: western_date)
30
+ GengouData.get_ancient_date(date: date)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,332 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nodoc:
4
+ module Zakuro
5
+ # :nodoc:
6
+ module Senmyou
7
+ #
8
+ # LunarOrbit 月軌道
9
+ #
10
+ module LunarOrbit
11
+ # @return [Integer] 暦周(1近日点)
12
+ ANOMALISTIC_MONTH = 231_458.19
13
+ # @return [Integer] 暦中日
14
+ # @note ANOMALISTIC_MONTH の半分に相当する
15
+ HALF_ANOMALISTIC_MONTH = \
16
+ LunarRemainder.new(day: 13, minute: 6529, second: 9.5)
17
+
18
+ #
19
+ # Adjustment 補正値情報
20
+ #
21
+ module Adjustment
22
+ #
23
+ # Item 補正値
24
+ #
25
+ class Item
26
+ # @return [Integer] 損益率
27
+ attr_reader :per
28
+ # @return [Integer] 眺朒(ちょうじく)積
29
+ attr_reader :stack
30
+
31
+ #
32
+ # 初期化
33
+ #
34
+ # @param [Integer] per 損益率
35
+ # @param [Integer] stack 眺朒(ちょうじく)積
36
+ #
37
+ def initialize(per:, stack:)
38
+ @per = per
39
+ @stack = stack
40
+ end
41
+
42
+ #
43
+ # 文字化
44
+ #
45
+ # @return [String] 文字
46
+ #
47
+ def to_s
48
+ "per:#{@per}, stack:#{@stack}"
49
+ end
50
+ end
51
+
52
+ # @return [Array<Item>] 月の補正値
53
+ # @note キーは複合キーであり、以下のパターンに対応する
54
+ # * 進退
55
+ # * forward: 進(遠地点より数える)
56
+ # * back: 退(近地点より数える)
57
+ # * 入暦(1-14)
58
+ # * 小余(0-8400)
59
+ LIST = {
60
+ forward_01_0000_8400: Item.new(per: +830, stack: 0),
61
+ forward_02_0000_8400: Item.new(per: +726, stack: +830),
62
+ forward_03_0000_8400: Item.new(per: +606, stack: +1556),
63
+ forward_04_0000_8400: Item.new(per: +471, stack: +2162),
64
+ forward_05_0000_8400: Item.new(per: +337, stack: +2633),
65
+ forward_06_0000_8400: Item.new(per: +202, stack: +2970),
66
+ forward_07_0000_7465: Item.new(per: +53, stack: +3172),
67
+ forward_07_7465_8400: Item.new(per: -7, stack: +3225), # +3172 + 53(初益)
68
+ forward_08_0000_8400: Item.new(per: -82, stack: +3218),
69
+ forward_09_0000_8400: Item.new(per: -224, stack: +3136),
70
+ forward_10_0000_8400: Item.new(per: -366, stack: +2912),
71
+ forward_11_0000_8400: Item.new(per: -509, stack: +2546),
72
+ forward_12_0000_8400: Item.new(per: -643, stack: +2037),
73
+ forward_13_0000_8400: Item.new(per: -748, stack: +1394),
74
+ forward_14_0000_6529: Item.new(per: -646, stack: +646), # 14日の小余は常に6529以下
75
+ back_01_0000_8400: Item.new(per: -830, stack: 0),
76
+ back_02_0000_8400: Item.new(per: -726, stack: -830),
77
+ back_03_0000_8400: Item.new(per: -598, stack: -1556),
78
+ back_04_0000_8400: Item.new(per: -464, stack: -2154),
79
+ back_05_0000_8400: Item.new(per: -329, stack: -2618),
80
+ back_06_0000_8400: Item.new(per: -195, stack: -2947),
81
+ back_07_0000_7465: Item.new(per: -53, stack: -3142),
82
+ back_07_7465_8400: Item.new(per: +7, stack: -3195), # -3142 - 53(初益)
83
+ back_08_0000_8400: Item.new(per: +82, stack: -3188),
84
+ back_09_0000_8400: Item.new(per: +225, stack: -3106),
85
+ back_10_0000_8400: Item.new(per: +366, stack: -2881),
86
+ back_11_0000_8400: Item.new(per: +501, stack: -2515),
87
+ back_12_0000_8400: Item.new(per: +628, stack: -2014),
88
+ back_13_0000_8400: Item.new(per: +740, stack: -1386),
89
+ back_14_0000_6529: Item.new(per: +646, stack: -646) # 14日の小余は常に6529以下
90
+ }.freeze
91
+ end
92
+
93
+ #
94
+ # 月の運行による補正値を算出する
95
+ #
96
+ # @param [Remainder] remainder_month 月の大余小余
97
+ # @param [True, False] is_forward 進(遠地点より数える)/退(近地点より数える)
98
+ #
99
+ # @return [Integer] 補正値
100
+ #
101
+ def self.calc_moon_orbit_value(remainder_month:, is_forward:)
102
+ valid?(remainder: remainder_month)
103
+
104
+ day, minute = make_calculable_remainder_value(remainder: remainder_month)
105
+
106
+ # 引き当て
107
+ adjustment, diff, minute = specify_moon_adjustment(
108
+ is_forward: is_forward, day: day, minute: minute
109
+ )
110
+ day = make_cumulative_value_for_days(per: adjustment.per,
111
+ denominator: diff, minute: minute)
112
+
113
+ adjustment.stack + day
114
+ end
115
+
116
+ #
117
+ # 大余小余を検証する
118
+ #
119
+ # @param [Remainder] remainder 大余小余
120
+ #
121
+ # @return [True] 正しい(月の位相計算に使う大余小余)
122
+ # @return [True] 正しくない
123
+ #
124
+ def self.valid?(remainder:)
125
+ return if remainder.is_a?(LunarRemainder)
126
+
127
+ raise ArgumentError, "unmatch parameter type: #{remainder.class}"
128
+ end
129
+ private_class_method :valid?
130
+
131
+ #
132
+ # 大余小余を計算可能な値にする
133
+ # @note 大余の秒(second)は使わない
134
+ #
135
+ # @param [LunarRemainder] remainder 大余小余
136
+ #
137
+ # @return [Integer] 大余
138
+ # @return [Integer] 小余
139
+ #
140
+ def self.make_calculable_remainder_value(remainder:)
141
+ day = remainder.day
142
+ minute = remainder.minute + (remainder.second / 100)
143
+ minute = minute.floor
144
+
145
+ [day, minute]
146
+ end
147
+ private_class_method :make_calculable_remainder_value
148
+
149
+ # :reek:TooManyStatements { max_statements: 9 }
150
+
151
+ #
152
+ # 累計値(大余)を作成する
153
+ #
154
+ # @param [Integer] per 入暦(1-14)
155
+ # @param [Integer] denominator 小余の分母
156
+ # @param [Integer] minute 小余
157
+ #
158
+ # @return [Integer] 累計値(大余)
159
+ #
160
+ def self.make_cumulative_value_for_days(per:, denominator:, minute:)
161
+ remainder_minute = (per * minute).to_f
162
+ day = remainder_minute / denominator
163
+ # 切り捨て(プラスマイナスに関わらず小数点以下切り捨て)
164
+ day = day.negative? ? day.ceil : day.floor
165
+ sign = remainder_minute.negative? ? -1 : 1
166
+ remainder_day = (sign * remainder_minute) % denominator
167
+ # 四捨五入(8400ならその半分の4200以上を繰り上げる)
168
+ day += sign if remainder_day >= (denominator / 2)
169
+
170
+ day
171
+ end
172
+ private_class_method :make_cumulative_value_for_days
173
+
174
+ # :reek:TooManyStatements { max_statements: 9 }
175
+
176
+ #
177
+ # 月軌道の補正に必要な基本値を引き当てる
178
+ #
179
+ # @note 戻り値は calc_moon_orbit_value で使用する
180
+ #
181
+ # @param [True, False] is_forward 進(遠地点より数える)/退(近地点より数える)
182
+ # @param [Integer] day 大余
183
+ # @param [Integer] minute 小余
184
+ #
185
+ # @return [Adjustment::Item] 補正値
186
+ # @return [Integer] (小余を処理する時の)分母
187
+ # @return [Integer] 小余の下げ幅
188
+ #
189
+ def self.specify_moon_adjustment(is_forward:, day:, minute:)
190
+ prefix = { true => 'forward', false => 'back' }[is_forward]
191
+
192
+ targets = Adjustment::LIST.select \
193
+ { |key, _| key.match(/^#{prefix}_#{format('%<day>02d', day: day)}_.*/) }
194
+
195
+ targets.each do |key, value|
196
+ # NOTE 境界値は上から順に引き当てた方を返す(7日の境界値7465は上のキーで返す)
197
+ matched, diff = \
198
+ extract_data_from_moon_adjustment_key(key, minute)
199
+ # 小余の下げ幅
200
+ calc_minute = (day == 7 && minute > 7465 ? minute - 7465 : minute)
201
+ return value, diff, calc_minute if matched
202
+ end
203
+
204
+ [nil, nil, nil]
205
+ end
206
+ private_class_method :specify_moon_adjustment
207
+
208
+ #
209
+ # 補正値を引き当てる
210
+ #
211
+ # @param [String] key 補正値のキー
212
+ # @param [Integer] minute 小余
213
+ #
214
+ # @return [Adjustment::Item] 補正値
215
+ # @return [Integer] (小余を処理する時の)分母
216
+ #
217
+ def self.extract_data_from_moon_adjustment_key(key, minute)
218
+ matched = key.match(/([0-9]{4})_([0-9]{4})$/)
219
+ start = matched[1].to_i
220
+ finish = matched[2].to_i
221
+
222
+ matched = minute >= start && minute <= finish
223
+ [matched, (finish - start)]
224
+ end
225
+ private_class_method :extract_data_from_moon_adjustment_key
226
+
227
+ # :reek:ControlParameter and :reek:BooleanParameter
228
+ # :reek:LongParameterList { max_params: 4 }
229
+
230
+ #
231
+ # 月地点を計算する
232
+ #
233
+ # @param [LunarRemainder] remainder 初回(昨年冬至), 前回計算結果(入暦)
234
+ # @param [Integer] western_year 西暦年
235
+ # @param [True, False] is_forward 進(遠地点より数える)/退(近地点より数える)
236
+ # @param [True, False] first 初回計算, 次回以降計算
237
+ #
238
+ # @return [LunarRemainder] 入暦
239
+ # @return [True] 進(遠地点より数える)
240
+ # @return [False] 退(近地点より数える)
241
+ #
242
+ def self.calc_moon_point(remainder:, western_year:,
243
+ is_forward: true, first: true)
244
+ if first
245
+ return calc_first_moon_point(winter_solstice_age: remainder,
246
+ western_year: western_year)
247
+ end
248
+ calc_following_moon_point(remainder: remainder, is_forward: is_forward)
249
+ end
250
+
251
+ # :reek:TooManyStatements { max_statements: 7 }
252
+
253
+ #
254
+ # 入暦(月の遠地点から数えた日数/近地点から数えた日数)を求める
255
+ #
256
+ # 天正冬至(入暦前回未計算)を求める
257
+ #
258
+ # @param [Remainder] winter_solstice_age 昨年冬至
259
+ # @param [Integer] western_year 西暦年
260
+ #
261
+ # @return [LunarRemainder] 入暦
262
+ # @return [True] 進(遠地点より数える)
263
+ # @return [False] 退(近地点より数える)
264
+ #
265
+ def self.calc_first_moon_point(winter_solstice_age:, western_year:)
266
+ # 積年の開始から対象年までの年数
267
+ total_year = \
268
+ WinterSolstice::TOTAL_YEAR + western_year - WinterSolstice::BEGIN_YEAR
269
+
270
+ # 通積分 - 天正閏余
271
+ total_day = \
272
+ total_year * WinterSolstice::YEAR - winter_solstice_age.to_minute
273
+
274
+ remainder_month = \
275
+ LunarRemainder.new(total: (total_day % ANOMALISTIC_MONTH))
276
+
277
+ remainder_month, is_forward = decrease_moon_point(
278
+ remainder_month: remainder_month,
279
+ remainder_limit: HALF_ANOMALISTIC_MONTH, is_forward: true
280
+ )
281
+
282
+ remainder_month.add!(Remainder.new(day: 1, minute: 0, second: 0))
283
+
284
+ [remainder_month, is_forward]
285
+ end
286
+ private_class_method :calc_first_moon_point
287
+
288
+ #
289
+ # 入暦(月の遠地点から数えた日数/近地点から数えた日数)を求める
290
+ #
291
+ # 前回計算結果を補正する
292
+ #
293
+ # @param [LunarRemainder] remainder 前回計算結果(入暦)
294
+ # @param [True, False] is_forward 進(遠地点より数える)/退(近地点より数える)
295
+ #
296
+ # @return [LunarRemainder] 入暦
297
+ # @return [True] 進(遠地点より数える)
298
+ # @return [False] 退(近地点より数える)
299
+ #
300
+ def self.calc_following_moon_point(remainder:, is_forward:)
301
+ # 前回計算結果を引き継いた場合、暦中日ではなく損益眺朒(ちょうじく)数の上限とする
302
+ remainder_month, is_forward = \
303
+ decrease_moon_point(
304
+ remainder_month: remainder,
305
+ remainder_limit: Remainder.new(day: 14, minute: 6529, second: 0),
306
+ is_forward: is_forward
307
+ )
308
+
309
+ [remainder_month, is_forward]
310
+ end
311
+ private_class_method :calc_following_moon_point
312
+
313
+ #
314
+ # 月地点を減算する
315
+ #
316
+ # @param [LunarRemainder] remainder_month 大余小余
317
+ # @param [Remainder] remainder_limit 大余小余の上限(最大の入暦)
318
+ # @param [True, False] is_forward 進(遠地点より数える)/退(近地点より数える)
319
+ #
320
+ # @return [LunarRemainder] 大余小余の減算結果(上限値超えした場合)
321
+ # @return [True] 進(遠地点より数える)
322
+ # @return [False] 退(近地点より数える)
323
+ #
324
+ def self.decrease_moon_point(remainder_month:, remainder_limit:, is_forward:)
325
+ return remainder_month, is_forward if remainder_month < remainder_limit
326
+
327
+ [remainder_month.sub(HALF_ANOMALISTIC_MONTH), !is_forward]
328
+ end
329
+ private_class_method :decrease_moon_point
330
+ end
331
+ end
332
+ end