# How long till Halley's Comet returns?

Halley's Comet is the only comet that may appear (i.e. become visible to the naked eye on Earth) twice in a human lifetime. The orbital period of Halley's Comet is not constant: it has varied between 75 to 79 Earth years since the first definite apparition was recorded in 240 BCE. This variability is mainly driven by gravitational interactions between the comet and the planets of the Solar System. For the three millennia up to the year 3000, Halley's Comet appeared, or is projected to appear, in the following years:

66, 141, 218, 295, 374, 451, 530, 607, 684, 760, 837, 912, 989, 1066, 1145, 1222, 1301, 1378, 1456, 1531, 1607, 1682, 1758, 1835, 1910, 1986, 2061, 2134, 2209, 2284, 2358, 2430, 2504, 2579, 2653, 2726, 2795, 2863, 2931, 3000

Apparitions beyond 2134 predicted using this orbit simulator. The simulations account for gravitational perturbations due to the planets but not for other effects, such as changes in the comet's outgassing rate, that also affect its orbit. See here for further discussion.

Write a program or function that takes as input a positive integer up to 3000 (inclusive), representing a year, and outputs/returns the number of years until the next apparition of Halley's Comet. In other words, if the input year is $$\y\$$ and Halley's Comet next appears in year $$\y_\text{H}\ge y\$$, find $$\y_\text{H}-y\$$. The shortest code (in bytes) in each language wins.

### Test cases

   1 -> 65
1066 ->  0
1067 -> 78
1986 ->  0
1987 -> 74
2021 -> 40


# JavaScript (Node.js), 74 bytes

n=>Buffer("BKMMOMOMMLMKMMOMOMNKLKLMKLKIKKJHJKJIEDDE").every(c=>(n-=c)>0)-n


Try it online!

### Commented

n =>              // n = input year
Buffer(         // build a buffer consisting of
"BKMM...EDDE" // the year deltas encoded as ASCII codes
)               // 'B' -> 66, + 'K' (75) -> 141, etc.
.every(c =>     // for each value c in there:
(n -= c)      //   subtract c from n
> 0           //   and go on while the result is positive
)               // end of every(), which yields false
- n             // return -n


# 05AB1E, 35 bytes

Uses second-order differences to reconstruct the years, which is 1 byte shorter than first-order differences.

•6?É@fô]öÔMƵ4^-•7в4-.¥75+.¥66+I-.Δd


Try it online!

•6...-•                    # compressed integer
7в                  # convert to base 7
4-                # subtract 4 from each
.¥              # un-delta: cumulative sum with a 0 prepended
75+           # add 75 to each; this is now the list of years between appearances
.¥         # un-delta
66+      # add 66 to each; this is now the list of years of appearances
I-    # subtract the input year
.Δd # find the first non-negative integer


Try it with step-by-step output!

The part .¥75+.¥66+ can be written with a loop at the same length as „KBÇv.¥y+}.

# R, 87 81 bytes

(z=cumsum(utf8ToInt("BKMMOMOMMLMKMMOMOMNKLKLMKLKIKKJHJKJIEDDE"))-scan())[z>=0][1]


Try it online!

# MATL, 45 44 bytes

66 75'#AeXj=*pq5 Oe>3-u'F7Za4-,hYs]i-t0<~)X<


### Explanation

This uses the second-order consecutive differences to compress the list of years.

66                   % Push 66
75                   % Push 75
'#AeXj=*pq5 Oe>3-u'  % Push this string
F7Za                 % Uncompress from base94 (printable ASCII except quote) to base 7
4-                   % Subtract 4. This is the 2nd-order difference of the list of years
,                    % Do twice
h                  %   Concatenate (first time with 75, second time with 66)
Ys                 %   Cumulative sum
]                    % End
i-                   % Subtract input element-wise
t                    % Duplicate
0<~                  % Logical index of non-negative values
)                    % Apply index to keep only non-negative values
X<                   % Minimum. Implicitly display


# Perl 5-p, 73 bytes

$,=EDDEIJKJHJKKIKLKMLKLKNMOMOMMKMLMMOMOMMKB;$_-=ord chop$,while$_>0;s/-//


Try it online!

Uses the popular ASCII encoding but puts the first appearance (year 66) at the right side.

# J, 61 bytes

(3+/\@u:'BKMMOMOMMLMKMMOMOMNKLKLMKLKIKKJHJKJIEDDE')&(]-~I.{[)


Try it online!

Noticed I independently came up with the same long constant Arnauld used in his answer, which is 66 prepended to the differences of the years, converted to Unicode code points u:.

The J code then converts back to a list of numbers 3 u:, and scan sums those numbers +/\@ to recover the original list of comet years.

We then find the "insert before" index I. of the program input within the list of comet years, and subtract the program input ]-~ from the actual comet list value at that same index I.{[.

# Pyth, 8542 41 bytes

-hg#Qt.u+NYCM+\B."BOÅ>+‘¸hbÊ¹¹dtg"Z


Try it online!

Managed to shed 44 bytes by using Arnauld's delta string.

# Wolfram Language (Mathematica), 80 bytes

-Fold[If[#>0,#-#2,#]&,#,36^^9cyu7y4a7m84jq6pihqeoy2jutbhh4~IntegerDigits~15+65]&


Try it online!

The comet should be visible near its periapsis. Unfortunately, CometData only includes the dates of the previous and next periapses (5 Feb 1986 and 31 May 2061).

Ｉ⌊Φ⁻ＥφΣＥ…”$↷↥CⅉＷＦ3⊖n↧JＮω∕υ³*Ｈ↨{Ｗ”⊕ι℅λＮ¬‹ι⁰  Try it online! Link is to verbose version of code. Also uses @Arnauld's string, although I didn't realise until I read @Jonah's answer. Explanation:  φ Predefined variable 1000 (shorter than 40) Ｅ Map over implicit range ”...” Compressed form of @Arnauld's string … Truncate to length ι Current index ⊕ Incremented Ｅ Map over characters λ Current character ℅ Take the ordinal Σ Summed ⁻ Vectorised subtract Ｎ Input year Φ Filter where ι Current value ¬‹ ⁰ Is non-negative ⌊ Take the minimum Ｉ Cast to string Implicitly print  # Husk, 47 bytes -⁰ḟ≥⁰∫m+66ΘB14B95mo-30c"(p_45Ux2|!uvrA(C$TDL3W


Try it online!

No built-in numeric compression in Husk, so we encode the differences between Halley years as printable ASCII values.

Halley year differences are then reconstructed by subtracting 30 from the ASCII values, converting to base 95, getting base 14 digits and adding 66. The Halley years are the cumulative sum of this: altogether ∫m+66ΘB14B95mo-30c"(p_45Ux2|!uvrA(C$TDL3W  (where "(p_45Ux2|!uvrA(C$TDL3W " is the encoding string).

We then just retrive the first element greater-or-equal to the input (ḟ≥⁰), and subtract the input from this (-⁰).

# Zsh, 78 bytes

for ((a=-$1;a<0;a+=##$s[++i]))s=BKMMOMOMMLMKMMOMOMNKLKLMKLKIKKJHJKJIEDDE;<<<$a  Try it online! I've had this answer prepared since this question was in the sandbox and I'm annoyed I missed it being posted Explanation: for ((a=-$1;a<0;a+=##$s[++i]))s=...;<<<$a

for ((     ;   ;            ))     ;       # C-style for loop
a=-$1; # initialise a to the input, negated a<0; # while a is less than 0 s=...; # set s to the string ++i # increment i (starts at 0 implicitly)$s[   ]               # get the ith character of s
##                      # get the codepoint number of that character
a+=                        # add that to a
;<<<$a # then print a  Note: Initially I had s=...;for ((...)):;<<<$r - setting s to the string beforehand, and the body of the loop being : (do nothing). But since the body of the loop is executed before the third clause of the loop (the "each iteration" part), and we only use s in that clause, we can put s in the body to save 2 bytes.

• Tip: If you 'follow' Sandbox posts you get a notification when the author modifies/deletes it. Jan 7 at 10:28

# Vyxal, 49 46 bytes

«¬{3₀M꘍½⁰⁽ȦǒE4⊍Ǔǎ⊍ƒ*qḋV⌊A«⇧vCKƛ∑66+;66p⁰-'0≥;h


Try it Online!

«...«                     # Base-255 compressed string
⇧                    # Uppercase
vC                  # Map to charcode
K                 # Get all prefixes
ƛ    ;           # Map to
∑66+            # Sum + 66
66p        # Prepend 66
⁰-      # Difference between input & number (vectorised)
'  ;  # Filter by
0≥   # Positive or 0
h # Get first item


Uses the first-order difference to encompass the years.

Thanks to lyxal for -3 bytes!

• ƛC; can simply become vC May 25 at 11:59
• Also ƛ⁰-; can just be ⁰- no? May 25 at 12:00

# 05AB1E, 71 bytes

•1Ò}MKX…иv½ι„qā·&à©Dà|ãвìkPn…(l2ßrÏyO;¬VÕÏcw÷©ûëтÎBʒNdãA₄ΩΓ•3001вʒI@}αW


Try it online!

I know there's a shorter 05AB1E answer, but this uses a different method, so I thought it'd be interesting to see the comparison in byte lengths.

## Explained

•...•3001в          # Pushed compressed list of all years
ʒI@}      # Keep only those >= the input year
αW    # Push the smallest absolute difference amongst those values


# Jelly, 34 bytes

“¡AMzⱮ⁽ƑẸÑ[NṀtR“¦Ṛ’b8+"6FŻ+66ÄḟḶḢ_


Try it online!

### How?

“...“¦Ṛ’b8+"6FŻ+66ÄḟḶḢ_ - Link: year
“...“¦Ṛ’                - list of base 250 integers = [18853782274514630143915309466833, 1683]
b8              - convert to base 8  = [[3, 5, 5, 7, ..., 2, 1], [3, 2, 2, 3]]
6           - six
+"            - zip (implicitly wrapped 6 = [6]) with addition
= [[9,11,11,13, ..., 8, 7], [3, 2, 2, 3]]
F          - flatten            = [ 9,11,11,13, ..., 8, 7,   3, 2, 2, 3]
Ż         - prepend zero    = [ 0, 9,11,11,13, ..., 8, 7,   3, 2, 2, 3]
+66      - add 66          = [66,75,77,77,79, ...,74,73,  69,68,68,69]
Ä     - cumulative sums = [66,141,218,295,374, ...,2653,2726,2795,2863,2931,3000]
Ḷ   - lowered range (year) = [0,1,2,...,year-1]
ḟ    - filter discard  - e.g. if year = 2860 then [2863,2931,3000]
_ - subtract (year)                                3


FWIW a port of ovs's 05AB1E answer using the second-difference (and also using the filter-out-lowered range technique above) is also 34:

“¬V>ƭṘḌƘH*O⁵i²Ọ’b7_4ÄŻ+75ÄŻ+66ḟḶḢ_

• Oops, thanks for the edit @caird! Jan 4 at 19:52

# Core Maude, 266263262 204 bytes

mod H is pr LIST{Nat}. op n : Nat ~> Nat . vars A B C : Nat . eq n(A)= n(A 66
5710855311372188087909795024940117969860147607). eq n(A B C)= if A > B then
n(A(B +(C & 15)+ 68)(C >> 4))else sd(A,B)fi . endm


### Example Session

Maude> red n(1) .  --- Ex: 65
result NzNat: 65
Maude> red n(1066) .  --- Ex: 0
result Zero: 0
Maude> red n(1067) .  --- Ex: 78
result NzNat: 78
Maude> red n(1986) .  --- Ex: 0
result Zero: 0
Maude> red n(2021) .  --- Ex: 40
result NzNat: 40
Maude> red n(3000) .  --- Ex: 0
result Zero: 0


### Ungolfed

mod H is
pr LIST{Nat} .

op n : Nat ~> Nat .

vars A B C : Nat .

eq n(A) = n(A 66 5710855311372188087909795024940117969860147607) .
eq n(A B C) =
if A > B
then n(A (B + (C & 15) + 68) (C >> 4))
else sd(A, B)
fi .
endm


The answer is obtained by reducing the function n, which takes the input year.

The large number encodes the differences between successive years (e.g., 141 - 66 = 75) minus 68, packed into a bitstring with 4 bits per value.

Shaved off 3 bytes by replacing two conditional equations with a single equation using if_then_else_fi`.

Saved one more byte by deduplicating a long enough repeated sublist using a condition.

Saved 58 bytes by encoding the year differences as a bitstring (and a few other tweaks).