module Collatz

Provides the basic functionality to interact with the Collatz conjecture. The parameterisation uses the same (p,a,b) notation as Conway’s generalisations. Besides the function and reverse function, there is also functionality to retrieve the hailstone sequence, the “stopping time”/“total stopping time”, or tree-graph.

Constants

KNOWN_CYCLES

The four known cycles for the standard parameterisation.

VERIFIED_MAXIMUM

The current value up to which the standard parameterisation has been verified.

VERIFIED_MINIMUM

The current value down to which the standard parameterisation has been verified.

VERSION

The current semver version.

Public Instance Methods

function(n, p: 2, a: 3, b: 1) click to toggle source

Returns the output of a single application of a Collatz-esque function.

@raise FailedSaneParameterCheck If p or a are 0.

@param Integer n: The value on which to perform the Collatz-esque function.

@param Integer p: Modulus used to devide n, iff n is equivalent to (0 mod p).

@param Integer a: Factor by which to multiply n.

@param Integer b: Value to add to the scaled value of n.

@return Integer The result of the function

# File lib/collatz/function.rb, line 49
def function(n, p: 2, a: 3, b: 1)
  assert_sane_parameterisation(p, a, b)
  (n%p).zero? ? (n/p) : ((a*n)+b)
end
hailstone_sequence(initial_value, p: 2, a: 3, b: 1, max_total_stopping_time: 1000, total_stopping_time: true) click to toggle source

Returns a list of successive values obtained by iterating a Collatz-esque function, until either 1 is reached, or the total amount of iterations exceeds max_total_stopping_time, unless total_stopping_time is False, which will terminate the hailstone at the “stopping time” value, i.e. the first value less than the initial value. While the sequence has the capability to determine that it has encountered a cycle, the cycle from “1” wont be attempted or reported as part of a cycle, regardless of default or custom parameterisation, as “1” is considered a “total stop”.

@raise FailedSaneParameterCheck If p or a are 0.

@param Integer initial_value: The value to begin the hailstone sequence from.

@param Integer p: Modulus used to devide n, iff n is equivalent to (0 mod p).

@param Integer a: Factor by which to multiply n.

@param Integer b: Value to add to the scaled value of n.

@param Integer max_total_stopping_time: Maximum amount of times to iterate the function, if 1 is not reached.

@param Boolean total_stopping_time: Whether or not to execute until the “total” stopping time (number of iterations to obtain 1) rather than the regular stopping time (number of iterations to reach a value less than the initial value).

@return HailstoneSequence A set of values that form the hailstone sequence.

# File lib/collatz/hailstone_sequence.rb, line 151
                def hailstone_sequence(initial_value, p: 2, a: 3, b: 1, max_total_stopping_time: 1000, total_stopping_time: true) # rubocop:disable Layout/LineLength
  # Prior to starting the hailstone, which has some magic case handling,
  # call the function to trigger any assert_sane_parameterisation flags.
  _throwaway = function(initial_value, p: p, a: a, b: b)
  # Return the hailstone sequence.
  HailstoneSequence.new(initial_value, p, a, b, max_total_stopping_time, total_stopping_time)
end
reverse_function(n, p: 2, a: 3, b: 1) click to toggle source

Returns the output of a single application of a Collatz-esque reverse function. If only one value is returned, it is the value that would be divided by p. If two values are returned, the first is the value that would be divided by p, and the second value is that which would undergo the multiply and add step, regardless of which is larger.

@raise FailedSaneParameterCheck If p or a are 0.

@param Integer n: The value on which to perform the reverse Collatz function.

@param Integer p: Modulus used to devide n, iff n is equivalent to (0 mod p).

@param Integer a: Factor by which to multiply n.

@param Integer b: Value to add to the scaled value of n.

@return +List<Integer>+ The values that would return the input if given to the function.

# File lib/collatz/function.rb, line 70
def reverse_function(n, p: 2, a: 3, b: 1)
  assert_sane_parameterisation(p, a, b)
  if ((n-b)%a).zero? && ((n-b)%(p*a)).nonzero?
    [p*n, (n-b)/a]
  else
    [p*n]
  end
end
stopping_time(initial_value, p: 2, a: 3, b: 1, max_stopping_time: 1000, total_stopping_time: false) click to toggle source

Returns the stopping time, the amount of iterations required to reach a value less than the initial value, or nil if max_stopping_time is exceeded. Alternatively, if total_stopping_time is true, then it will instead count the amount of iterations to reach 1. If the sequence does not stop, but instead ends in a cycle, the result will be infinity. If (p,a,b) are such that it is possible to get stuck on zero, the result will be the negative of what would otherwise be the “total stopping time” to reach 1, where 0 is considered a “total stop” that should not occur as it does form a cycle of length 1.

@raise FailedSaneParameterCheck If p or a are 0.

@param Integer initial_value: The value for which to find the stopping time.

@param Integer p: Modulus used to devide n, iff n is equivalent to (0 mod p).

@param Integer a: Factor by which to multiply n.

@param Integer b: Value to add to the scaled value of n.

@param Integer max_stopping_time: Maximum amount of times to iterate the function, if the stopping time is not reached. IF the max_stopping_time is reached, the function will return nil.

@param Boolean total_stopping_time: Whether or not to execute until the “total” stopping time (number of iterations to obtain 1) rather than the regular stopping time (number of iterations to reach a value less than the initial value).

@return Integer The stopping time, or, in a special case, infinity, nil or a negative.

# File lib/collatz/hailstone_sequence.rb, line 188
                def stopping_time(initial_value, p: 2, a: 3, b: 1, max_stopping_time: 1000, total_stopping_time: false) # rubocop:disable Layout/LineLength
  # Although the "max_~_time" for hailstones is named for "total stopping" time
  # and the "max_~_time" for this "stopping time" function is _not_ "total",
  # they are handled the same way, as the default for "total_stopping_time"
  # for hailstones is true, but for this, is false. Thus the naming difference.
  # rubocop:disable Layout/HashAlignment
  hail = hailstone_sequence(initial_value, p: p, a: a, b: b,
                            max_total_stopping_time: max_stopping_time,
                            total_stopping_time: total_stopping_time)
  # rubocop:enable Layout/HashAlignment
  # For total/regular/zero stopping time, the value is already the same as
  # that present, for cycles we report infinity instead of the cycle length,
  # and for max stop out of bounds, we report None instead of the max stop cap
  { SequenceState::TOTAL_STOPPING_TIME => hail.terminal_status,
    SequenceState::STOPPING_TIME => hail.terminal_status,
    SequenceState::CYCLE_LENGTH => Float::INFINITY,
    SequenceState::ZERO_STOP => hail.terminal_status,
    SequenceState::MAX_STOP_OUT_OF_BOUNDS => nil
  }.fetch(hail.terminal_condition, nil)
end
tree_graph(initial_value, max_orbit_distance, p: 2, a: 3, b: 1) click to toggle source

Returns a directed tree graph of the reverse function values up to a maximum nesting of max_orbit_distance, with the initial_value as the root.

@raise FailedSaneParameterCheck If p or a are 0.

@param Integer initial_value: The root value of the directed tree graph.

@param Integer max_orbit_distance: Maximum amount of times to iterate the reverse function. There is no natural termination to populating the tree graph, equivalent to the termination of hailstone sequences or stopping time attempts, so this is not an optional argument like max_stopping_time / max_total_stopping_time, as it is the intended target of orbits to obtain, rather than a limit to avoid uncapped computation.

@param Integer p: Modulus used to devide n, iff n is equivalent to (0 mod p).

@param Integer a: Factor by which to multiply n.

@param Integer b: Value to add to the scaled value of n.

@return TreeGraph The branches of the tree graph as determined by the reverse function.

# File lib/collatz/tree_graph.rb, line 177
                def tree_graph(initial_value, max_orbit_distance, p: 2, a: 3, b: 1)
  # Prior to starting the tree graph, which has some magic case handling, call
  # the reverse_function to trigger any assert_sane_parameterisation flags.
  _throwaway = reverse_function(initial_value, p: p, a: a, b: b)
  TreeGraph.new(initial_value, max_orbit_distance, p, a, b)
end