collatz 0.1.0 → 1.1.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.
data/lib/collatz.rb CHANGED
@@ -1,237 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "collatz/version"
4
+ require_relative "collatz/constants"
5
+ require_relative "collatz/utilities"
6
+ require_relative "collatz/function"
7
+ require_relative "collatz/hailstone_sequence"
8
+ require_relative "collatz/tree_graph"
4
9
 
5
- # :section: Main
6
10
  # Provides the basic functionality to interact with the Collatz conjecture. The
7
11
  # parameterisation uses the same (p,a,b) notation as Conway's generalisations.
8
12
  # Besides the function and reverse function, there is also functionality to
9
13
  # retrieve the hailstone sequence, the "stopping time"/"total stopping time", or
10
14
  # tree-graph.
15
+ module Collatz
16
+ # Using a module to proctor a namespace for the functions, none of which
17
+ # are instance methods. All are "class" methods, so set module_function
18
+ # at the head of each required file which opens this module and has defs.
19
+ # https://github.com/rubocop/ruby-style-guide#modules-vs-classes
20
+ # rubocop:disable Lint/UselessAccessModifier
11
21
 
12
- # The four known cycles for the standard parameterisation.
13
- KNOWN_CYCLES = [
14
- [1, 4, 2].freeze, [-1, -2].freeze, [-5, -14, -7, -20, -10].freeze,
15
- [-17, -50, -25, -74, -37, -110, -55, -164, -82, -41, -122, -61, -182, -91, -272, -136, -68, -34].freeze
16
- ].freeze
17
- # The current value up to which the standard parameterisation has been verified.
18
- VERIFIED_MAXIMUM = 295147905179352825856
19
- # The current value down to which the standard parameterisation has been verified.
20
- VERIFIED_MINIMUM = -272 # TODO: Check the actual lowest bound.
22
+ module_function # rubocop:disable Style/AccessModifierDeclarations
21
23
 
22
- # Error message constants, to be used as input to the FailedSaneParameterCheck.
23
- module SaneParameterErrMsg
24
- # Message to print in the FailedSaneParameterCheck if p, the modulus, is zero.
25
- SANE_PARAMS_P = "'p' should not be 0 ~ violates modulo being non-zero."
26
- # Message to print in the FailedSaneParameterCheck if a, the multiplicand, is zero.
27
- SANE_PARAMS_A = "'a' should not be 0 ~ violates the reversability."
28
- end
29
-
30
- # Thrown when either p, the modulus, or a, the multiplicand, are zero.
31
- class FailedSaneParameterCheck < StandardError
32
- # Construct a FailedSaneParameterCheck with a message associated with the provided enum.
33
- # @param [String] msg One of the SaneParameterErrMsg strings.
34
- def initialize(msg = "This is a custom exception", exception_type = "FailedSaneParameterCheck")
35
- @exception_type = exception_type
36
- super(msg)
37
- end
38
- end
39
-
40
- # SequenceState for Cycle Control: Descriptive flags to indicate when some event occurs in the
41
- # hailstone sequences or tree graph reversal, when set to verbose, or stopping time check.
42
- module SequenceState
43
- # A Hailstone sequence state that indicates the stopping
44
- # time, a value less than the initial, has been reached.
45
- STOPPING_TIME = "STOPPING_TIME"
46
- # A Hailstone sequence state that indicates the total
47
- # stopping time, a value of 1, has been reached.
48
- TOTAL_STOPPING_TIME = "TOTAL_STOPPING_TIME"
49
- # A Hailstone and TreeGraph sequence state that indicates the
50
- # first occurence of a value that subsequently forms a cycle.
51
- CYCLE_INIT = "CYCLE_INIT"
52
- # A Hailstone and TreeGraph sequence state that indicates the
53
- # last occurence of a value that has already formed a cycle.
54
- CYCLE_LENGTH = "CYCLE_LENGTH"
55
- # A Hailstone and TreeGraph sequence state that indicates the sequence
56
- # or traversal has executed some imposed 'maximum' amount of times.
57
- MAX_STOP_OUT_OF_BOUNDS = "MAX_STOP_OUT_OF_BOUNDS"
58
- # A Hailstone sequence state that indicates the sequence terminated
59
- # by reaching "0", a special type of "stopping time".
60
- ZERO_STOP = "ZERO_STOP"
61
- end
62
-
63
- # Handles the sanity check for the parameterisation (p,a,b)
64
- # required by both the function and reverse function.
65
- #
66
- # @raise [FailedSaneParameterCheck] If p or a are 0.
67
- #
68
- # @param [Integer] p Modulus used to devide n, iff n is equivalent to (0 mod p)
69
- # @param [Integer] a Factor by which to multiply n.
70
- # @param [Integer] b Value to add to the scaled value of n.
71
- def assert_sane_parameterisation(p, a, _b)
72
- # Sanity check (p,a,b) ~ p absolutely can't be 0. a "could" be zero
73
- # theoretically, although would violate the reversability (if ~a is 0 then a
74
- # value of "b" as the input to the reverse function would have a pre-emptive
75
- # value of every number not divisible by p). The function doesn't _have_ to
76
- # be reversable, but we are only interested in dealing with the class of
77
- # functions that exhibit behaviour consistant with the collatz function. If
78
- # _every_ input not divisable by p went straight to "b", it would simply
79
- # cause a cycle consisting of "b" and every b/p^z that is an integer. While
80
- # p in [-1, 1] could also be a reasonable check, as it makes every value
81
- # either a 1 or 2 length cycle, it's not strictly an illegal operation.
82
- # "b" being zero would cause behaviour not consistant with the collatz
83
- # function, but would not violate the reversability, so no check either.
84
- # " != 0" is redundant for python assertions.
85
- raise FailedSaneParameterCheck, SaneParameterErrMsg::SANE_PARAMS_P unless p != 0
86
- raise FailedSaneParameterCheck, SaneParameterErrMsg::SANE_PARAMS_A unless a != 0
87
- end
88
-
89
- private :assert_sane_parameterisation
90
-
91
- # Returns the output of a single application of a Collatz-esque function.
92
- #
93
- # @raise [FailedSaneParameterCheck] If p or a are 0.
94
- #
95
- # @param [Integer] n The value on which to perform the Collatz-esque function.
96
- # @param [Integer] p Modulus used to devide n, iff n is equivalent to (0 mod p).
97
- # @param [Integer] a Factor by which to multiply n.
98
- # @param [Integer] b Value to add to the scaled value of n.
99
- #
100
- # @return [Integer] The result of the function
101
- def function(n, p: 2, a: 3, b: 1)
102
- assert_sane_parameterisation(p, a, b)
103
- (n%p).zero? ? (n/p) : ((a*n)+b)
104
- end
105
-
106
- # Returns the output of a single application of a Collatz-esque reverse function. If
107
- # only one value is returned, it is the value that would be divided by p. If two values
108
- # are returned, the first is the value that would be divided by p, and the second value
109
- # is that which would undergo the multiply and add step, regardless of which is larger.
110
- #
111
- # @raise [FailedSaneParameterCheck] If p or a are 0.
112
- #
113
- # @param [Integer] n The value on which to perform the reverse Collatz function.
114
- # @param [Integer] p Modulus used to devide n, iff n is equivalent to (0 mod p).
115
- # @param [Integer] a Factor by which to multiply n.
116
- # @param [Integer] b Value to add to the scaled value of n.
117
- #
118
- # @return [Integer] The values that would return the input if given to the function.
119
- def reverse_function(n, p: 2, a: 3, b: 1)
120
- assert_sane_parameterisation(p, a, b)
121
- pre_values = [p*n]
122
- pre_values += [(n-b)/a] if ((n-b)%a).zero? && !((n-b)%(p*a)).zero?
123
- pre_values
124
- end
125
-
126
- # Provides the appropriate lambda to use to check if iterations on an initial
127
- # value have reached either the stopping time, or total stopping time.
128
- # @param [Integer] n The initial value to confirm against a stopping time check.
129
- # @param [Boolean] total_stop If false, the lambda will confirm that iterations of n
130
- # have reached the oriented stopping time to reach a value closer to 0.
131
- # If true, the lambda will simply check equality to 1.
132
- # @return [Method(Integer)->(Boolean)] The lambda to check for the stopping time.
133
- def stopping_time_terminus(n, total_stop)
134
- raise NotImplementedError, "Will be implemented at, or before, v1.0.0"
135
- end
136
-
137
- private :stopping_time_terminus
138
-
139
- # Contains the results of computing a hailstone sequence via hailstone_sequence(~).
140
- class HailstoneSequence
141
- def initialize
142
- raise NotImplementedError, "Will be implemented at, or before, v1.0.0"
143
- end
144
- end
145
-
146
- # Returns a list of successive values obtained by iterating a Collatz-esque
147
- # function, until either 1 is reached, or the total amount of iterations
148
- # exceeds max_total_stopping_time, unless total_stopping_time is False,
149
- # which will terminate the hailstone at the "stopping time" value, i.e. the
150
- # first value less than the initial value. While the sequence has the
151
- # capability to determine that it has encountered a cycle, the cycle from "1"
152
- # wont be attempted or reported as part of a cycle, regardless of default or
153
- # custom parameterisation, as "1" is considered a "total stop".
154
- #
155
- # @raise [FailedSaneParameterCheck] If p or a are 0.
156
- #
157
- # @param [Integer] initial_value The value to begin the hailstone sequence from.
158
- # @param [Integer] p Modulus used to devide n, iff n is equivalent to (0 mod p).
159
- # @param [Integer] a Factor by which to multiply n.
160
- # @param [Integer] b Value to add to the scaled value of n.
161
- # @param [Integer] max_total_stopping_time Maximum amount of times to iterate the function, if 1 is not reached.
162
- # @param [Boolean] total_stopping_time Whether or not to execute until the "total" stopping time
163
- # (number of iterations to obtain 1) rather than the regular stopping time (number
164
- # of iterations to reach a value less than the initial value).
165
- #
166
- # @return [HailstoneSequence] A set of values that form the hailstone sequence.
167
- def hailstone_sequence(initial_value, p: 2, a: 3, b: 1, max_total_stopping_time: 1000, total_stopping_time: true)
168
- raise NotImplementedError, "Will be implemented at, or before, v1.0.0"
169
- end
170
-
171
- # Returns the stopping time, the amount of iterations required to reach a
172
- # value less than the initial value, or nil if max_stopping_time is exceeded.
173
- # Alternatively, if total_stopping_time is True, then it will instead count
174
- # the amount of iterations to reach 1. If the sequence does not stop, but
175
- # instead ends in a cycle, the result will be infinity.
176
- # If (p,a,b) are such that it is possible to get stuck on zero, the result
177
- # will be the negative of what would otherwise be the "total stopping time"
178
- # to reach 1, where 0 is considered a "total stop" that should not occur as
179
- # it does form a cycle of length 1.
180
- #
181
- # @raise [FailedSaneParameterCheck] If p or a are 0.
182
- #
183
- # @param [Integer] initial_value The value for which to find the stopping time.
184
- # @param [Integer] p Modulus used to devide n, iff n is equivalent to (0 mod p).
185
- # @param [Integer] a Factor by which to multiply n.
186
- # @param [Integer] b Value to add to the scaled value of n.
187
- # @param [Integer] max_stopping_time Maximum amount of times to iterate the function, if
188
- # the stopping time is not reached. IF the max_stopping_time is reached,
189
- # the function will return nil.
190
- # @param [Boolean] total_stopping_time Whether or not to execute until the "total" stopping
191
- # time (number of iterations to obtain 1) rather than the regular stopping
192
- # time (number of iterations to reach a value less than the initial value).
193
- #
194
- # @return [Integer] The stopping time, or, in a special case, infinity, nil or a negative.
195
- def stopping_time(initial_value, p: 2, a: 3, b: 1, max_stopping_time: 1000, total_stopping_time: false)
196
- raise NotImplementedError, "Will be implemented at, or before, v1.0.0"
197
- end
198
-
199
- # Nodes that form a "tree graph", structured as a tree, with their own node's value,
200
- # as well as references to either possible child node, where a node can only ever have
201
- # two children, as there are only ever two reverse values. Also records any possible
202
- # "terminal sequence state", whether that be that the "orbit distance" has been reached,
203
- # as an "out of bounds" stop, which is the regularly expected terminal state. Other
204
- # terminal states possible however include the cycle state and cycle length (end) states.
205
- class TreeGraphNode
206
- def initialize
207
- raise NotImplementedError, "Will be implemented at, or before, v1.0.0"
208
- end
209
- end
210
-
211
- # Contains the results of computing the Tree Graph via tree_graph(~).
212
- # Contains the root node of a tree of TreeGraphNode's.
213
- class TreeGraph
214
- def initialize
215
- raise NotImplementedError, "Will be implemented at, or before, v1.0.0"
216
- end
217
- end
218
-
219
- # Returns a directed tree graph of the reverse function values up to a maximum
220
- # nesting of max_orbit_distance, with the initial_value as the root.
221
- #
222
- # @raise [FailedSaneParameterCheck] If p or a are 0.
223
- #
224
- # @param [Integer] initial_value The root value of the directed tree graph.
225
- # @param [Integer] max_orbit_distance Maximum amount of times to iterate the reverse
226
- # function. There is no natural termination to populating the tree graph, equivalent
227
- # to the termination of hailstone sequences or stopping time attempts, so this is not
228
- # an optional argument like max_stopping_time / max_total_stopping_time, as it is the intended
229
- # target of orbits to obtain, rather than a limit to avoid uncapped computation.
230
- # @param [Integer] p Modulus used to devide n, iff n is equivalent to (0 mod p).
231
- # @param [Integer] a Factor by which to multiply n.
232
- # @param [Integer] b Value to add to the scaled value of n.
233
- #
234
- # @return [TreeGraph] The branches of the tree graph as determined by the reverse function.
235
- def tree_graph(initial_value, max_orbit_distance, p: 2, a: 3, b: 1, __cycle_prevention: nil)
236
- raise NotImplementedError, "Will be implemented at, or before, v1.0.0"
24
+ # rubocop:enable Lint/UselessAccessModifier
237
25
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: collatz
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Levett
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-09-21 00:00:00.000000000 Z
11
+ date: 2025-02-23 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |-
14
14
  Provides the basic functionality to interact with the Collatz conjecture.
@@ -24,7 +24,7 @@ extra_rdoc_files: []
24
24
  files:
25
25
  - ".rspec"
26
26
  - ".rubocop.yml"
27
- - BUNDLER.md
27
+ - ".vscode/extensions.json"
28
28
  - Gemfile
29
29
  - Gemfile.lock
30
30
  - LICENSE
@@ -34,6 +34,11 @@ files:
34
34
  - collatz.gemspec
35
35
  - devlog.md
36
36
  - lib/collatz.rb
37
+ - lib/collatz/constants.rb
38
+ - lib/collatz/function.rb
39
+ - lib/collatz/hailstone_sequence.rb
40
+ - lib/collatz/tree_graph.rb
41
+ - lib/collatz/utilities.rb
37
42
  - lib/collatz/version.rb
38
43
  homepage: https://skenvy.github.io/Collatz/ruby/
39
44
  licenses:
@@ -49,14 +54,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
49
54
  requirements:
50
55
  - - ">="
51
56
  - !ruby/object:Gem::Version
52
- version: 2.7.0
57
+ version: 3.0.0
53
58
  required_rubygems_version: !ruby/object:Gem::Requirement
54
59
  requirements:
55
60
  - - ">="
56
61
  - !ruby/object:Gem::Version
57
62
  version: '0'
58
63
  requirements: []
59
- rubygems_version: 3.1.6
64
+ rubygems_version: 3.5.23
60
65
  signing_key:
61
66
  specification_version: 4
62
67
  summary: Functions Related to the Collatz/Syracuse/3n+1 Problem
data/BUNDLER.md DELETED
@@ -1,33 +0,0 @@
1
- # Collatz
2
-
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/collatz`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
6
-
7
- ## Installation
8
-
9
- Install the gem and add to the application's Gemfile by executing:
10
-
11
- $ bundle add collatz
12
-
13
- If bundler is not being used to manage dependencies, install the gem by executing:
14
-
15
- $ gem install collatz
16
-
17
- ## Usage
18
-
19
- TODO: Write usage instructions here
20
-
21
- ## Development
22
-
23
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
24
-
25
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
26
-
27
- ## Contributing
28
-
29
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/collatz. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/collatz/blob/master/CODE_OF_CONDUCT.md).
30
-
31
- ## Code of Conduct
32
-
33
- Everyone interacting in the Collatz project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/collatz/blob/master/CODE_OF_CONDUCT.md).