vsop87/
lib.rs

1//! This library implements the *VSOP87* solutions to calculate the positions of the planets in the
2//! solar system.
3//!
4//! The main module calculates heliocentric ecliptic orbital elements for the equinox J2000.0 for
5//! the planets in the solar system, the basic *VSOP87* solution. There is one module per other
6//! *VSOP87* implementation: [*VSOP87A*](./vsop87a/index.html), [*VSOP87B*](./vsop87b/index.html),
7//! [*VSOP87C*](./vsop87c/index.html), [*VSOP87D*](./vsop87d/index.html) and
8//! [*VSOP87E*](./vsop87e/index.html). More information can be found
9//! [here](https://www.caglow.com/info/compute/vsop87) and
10//! [here](https://en.wikipedia.org/wiki/VSOP_(planets)).
11//!
12//! Each module has its own documentation, and here is the documentation on the base *VSOP87*
13//! solution. The *VSOP87* algorithm has great precission (under 1") for **4,000 years** before and
14//! after J2000 epoch for Mercury, Venus, Earth-Moon barycenter and Mars, for **2,000 years** in
15//! the case of Jupiter and Saturn and for **6,000 years** for Uranus and Neptune.
16//!
17//! The base *VSOP87* solution calculates the
18//! [orbital elements](https://en.wikipedia.org/wiki/Orbital_elements) of the planets arount the
19//! Sun. The returned elements are a special VSOP87 orbital elements, that can be converted into
20//! usual keplerian elements using the `Into` trait. These elements are ideal to get an idea on how
21//! the orbits are changing over time. It can also be used for other complex orbital computations.
22//!
23//! # Example
24//!
25//! As an example, here we calculate the orbital parameters for Mercury on the January 1st, 2000.
26//! The *VSOP87* algorithm requires dates to be entered as
27//! [Julian Day](https://en.wikipedia.org/wiki/Julian_day) (*JD*). In our case, that date is
28//! `2451545.0`.
29//!
30//! We first calculate the VSOP87 elements:
31//!
32//! ```
33//! let vsop87_elts = vsop87::mercury(2451545.0);
34//!
35//! assert!(vsop87_elts.a > 0.3870982121 && vsop87_elts.a < 0.3870982123);
36//! assert!(vsop87_elts.l > 4.4026057778 && vsop87_elts.l < 4.4026057780);
37//! assert!(vsop87_elts.k > 0.0446647517 && vsop87_elts.k < 0.0446647519);
38//! assert!(vsop87_elts.h > 0.2007208957 && vsop87_elts.h < 0.2007208959);
39//! assert!(vsop87_elts.q > 0.0406161540 && vsop87_elts.q < 0.0406161542);
40//! assert!(vsop87_elts.p > 0.04563512 && vsop87_elts.p < 0.04563588);
41//! ```
42//!
43//! Note that the `>` and `<` comparisons are there because floats should not be compared using
44//! `==`. Those numbers are retrieved from the test data of the *VSOP87* algorithm.
45//!
46//! We can then convert them into keplerian elements, by using both `KeplerianElements::from()` or
47//! the `into()` function in the *VSOP87* elements. This also works the other way around:
48//!
49//! ```
50//! use vsop87::{KeplerianElements, VSOP87Elements};
51//!
52//! # let vsop87_elts = vsop87::mercury(2451545.0);
53//! #
54//! # assert!(vsop87_elts.a > 0.3870982121 && vsop87_elts.a < 0.3870982123);
55//! # assert!(vsop87_elts.l > 4.4026057778 && vsop87_elts.l < 4.4026057780);
56//! # assert!(vsop87_elts.k > 0.0446647517 && vsop87_elts.k < 0.0446647519);
57//! # assert!(vsop87_elts.h > 0.2007208957 && vsop87_elts.h < 0.2007208959);
58//! # assert!(vsop87_elts.q > 0.0406161540 && vsop87_elts.q < 0.0406161542);
59//! # assert!(vsop87_elts.p > 0.04563512 && vsop87_elts.p < 0.04563588);
60//! #
61//! let elements = KeplerianElements::from(vsop87_elts);
62//! let convert_back: VSOP87Elements = elements.into();
63//!
64//! assert!(elements.semimajor_axis() > 0.387097 && elements.semimajor_axis() < 0.387099);
65//! assert!(elements.eccentricity() > 0.205629 && elements.eccentricity() < 0.205631);
66//! assert!(elements.inclination() > 0.122260 && elements.inclination() < 0.122261);
67//! assert!(elements.ascending_node() > 0.843525 && elements.ascending_node() < 0.843527);
68//! assert!(elements.periapsis() > 1.35183 && elements.periapsis() < 1.35185);
69//! assert!(elements.mean_anomaly() > 4.40259 && elements.mean_anomaly() < 4.40261);
70//! ```
71//!
72//! As you can see, these numbers perfectly match
73//! [those from NASA](http://solarsystem.nasa.gov/planets/mercury/facts).
74
75#![forbid(
76    missing_docs,
77    anonymous_parameters,
78    unused,
79    missing_copy_implementations,
80    trivial_casts,
81    variant_size_differences,
82    missing_debug_implementations,
83    trivial_numeric_casts,
84    unused,
85    rust_2018_compatibility,
86    rust_2018_idioms,
87    rust_2021_compatibility
88)]
89#![cfg_attr(not(test), forbid(clippy::unwrap_used))]
90#![deny(unused_qualifications, unsafe_code)]
91#![warn(
92    // rustc lint groups https://doc.rust-lang.org/rustc/lints/groups.html
93    warnings,
94    future_incompatible,
95    let_underscore,
96    nonstandard_style,
97
98    // rustc allowed-by-default lints https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html
99    macro_use_extern_crate,
100    meta_variable_misuse,
101    missing_abi,
102    non_ascii_idents,
103    noop_method_call,
104    single_use_lifetimes,
105    unreachable_pub,
106    unsafe_op_in_unsafe_fn,
107    unused_crate_dependencies,
108    unused_lifetimes,
109    unused_qualifications,
110    unused_tuple_struct_fields,
111
112    // rustdoc lints https://doc.rust-lang.org/rustdoc/lints.html
113    rustdoc::broken_intra_doc_links,
114    rustdoc::private_intra_doc_links,
115    rustdoc::missing_crate_level_docs,
116    rustdoc::private_doc_tests,
117    rustdoc::invalid_codeblock_attributes,
118    rustdoc::invalid_rust_codeblocks,
119    rustdoc::bare_urls,
120
121    // clippy allowed by default
122    clippy::dbg_macro,
123
124    // clippy categories https://doc.rust-lang.org/clippy/
125    clippy::all,
126    clippy::correctness,
127    clippy::suspicious,
128    clippy::style,
129    clippy::complexity,
130    clippy::perf,
131    clippy::pedantic,
132)]
133#![allow(
134    // Currently throws a false positive regarding dependencies that are only used in benchmarks.
135    unused_crate_dependencies,
136    clippy::module_name_repetitions,
137    clippy::redundant_pub_crate,
138    clippy::too_many_lines,
139    clippy::cognitive_complexity,
140    clippy::missing_errors_doc,
141    clippy::let_unit_value,
142    clippy::option_if_let_else,
143
144    // It may be worth to look if we can fix the issues highlighted by these lints.
145    clippy::cast_possible_truncation,
146    clippy::cast_sign_loss,
147    clippy::cast_precision_loss,
148    clippy::cast_possible_wrap,
149
150    // Add temporarily - Needs addressing
151    clippy::missing_panics_doc,
152)]
153#![allow(
154    clippy::many_single_char_names,
155    clippy::unreadable_literal,
156    clippy::excessive_precision
157)]
158// #![cfg_attr(all(test, feature = "no_std"), allow(unused_imports))]
159// Features
160#![cfg_attr(feature = "no_std", no_std)]
161// All the "allow by default" lints
162#![warn(box_pointers, unused_results)]
163
164pub mod vsop87a;
165pub mod vsop87b;
166pub mod vsop87c;
167pub mod vsop87d;
168pub mod vsop87e;
169
170mod earth_moon;
171mod jupiter;
172mod mars;
173mod mercury;
174mod neptune;
175mod saturn;
176mod uranus;
177mod venus;
178
179#[cfg(feature = "no_std")]
180use core::f64::consts::PI;
181#[cfg(feature = "no_std")]
182use libm::{acos, asin, atan, cos, sin, sincos, sqrt};
183
184#[cfg(not(feature = "no_std"))]
185use std::f64::consts::PI;
186
187/// Structure representing the keplerian elements of an orbit.
188#[derive(Debug, Clone, Copy, PartialEq)]
189pub struct KeplerianElements {
190    ecc: f64,
191    sma: f64,
192    incl: f64,
193    lan: f64,
194    lper: f64,
195    l0: f64,
196}
197
198impl KeplerianElements {
199    /// Gets the eccentricity of the orbit (*e*).
200    ///
201    /// A number smaller to one would be a closed ellipse, while 0 would be a circle orbit. Values
202    /// higher than one would be hyperbolic orbits, that are not closed, while a 1 would be a
203    /// parabolic orbit. Negative values cannot exist.
204    #[must_use]
205    pub fn eccentricity(&self) -> f64 {
206        self.ecc
207    }
208
209    /// Gets the semimajor axis of an orbit (*a*), in *AU* (Astronomical units).
210    ///
211    /// This value represents the average distance from the orbiting body to the center of mass.
212    #[must_use]
213    pub fn semimajor_axis(&self) -> f64 {
214        self.sma
215    }
216
217    /// Gets the inclination of the orbit (*i*), in radians.
218    ///
219    /// This value represents the inclination of the plane where the object is orbiting with
220    /// respect to the reference plane.
221    #[must_use]
222    pub fn inclination(&self) -> f64 {
223        self.incl
224    }
225
226    /// Gets the longitude of the ascending node of the orbit (*Ω*), in radians.
227    ///
228    /// This value represents the angle in the orbit ellipse of the point where the reference plane
229    /// and the orbit plane cross when the orbiting body crosses the plane *ascending* in the orbit.
230    #[must_use]
231    pub fn ascending_node(&self) -> f64 {
232        self.lan
233    }
234
235    /// Gets the longitude of the periapsis of the orbit (*ϖ*), in radians.
236    ///
237    /// This value represents the angle in the orbit ellipse of the nearest point of the orbit to
238    /// the center of mass of the system.
239    #[must_use]
240    pub fn periapsis(&self) -> f64 {
241        self.lper
242    }
243
244    /// Gets the mean anomaly of the orbiting object at the given epoch.
245    ///
246    /// This value represents the angle in the orbit ellipse of the orbiting body at the given
247    /// moment.
248    #[must_use]
249    pub fn mean_anomaly(&self) -> f64 {
250        self.l0
251    }
252}
253
254impl From<VSOP87Elements> for KeplerianElements {
255    #[inline]
256    fn from(elts: VSOP87Elements) -> Self {
257        #[cfg(feature = "no_std")]
258        {
259            let ecc = sqrt(elts.h * elts.h + elts.k * elts.k);
260            let i = acos(1_f64 - 2_f64 * (elts.p * elts.p + elts.q * elts.q));
261            let lan = atan(elts.p / elts.q);
262            let lper = asin(elts.h / ecc);
263
264            Self {
265                ecc,
266                sma: elts.a,
267                incl: i,
268                lan,
269                lper,
270                l0: elts.l,
271            }
272        }
273
274        #[cfg(not(feature = "no_std"))]
275        {
276            let ecc = (elts.h * elts.h + elts.k * elts.k).sqrt();
277            let i = (1_f64 - 2_f64 * (elts.p * elts.p + elts.q * elts.q)).acos();
278            let lan = (elts.p / elts.q).atan();
279            let lper = (elts.h / ecc).asin();
280
281            Self {
282                ecc,
283                sma: elts.a,
284                incl: i,
285                lan,
286                lper,
287                l0: elts.l,
288            }
289        }
290    }
291}
292
293/// Structure representing 3 dimensional rectangular coordinates.
294#[derive(Debug, Clone, Copy, PartialEq)]
295pub struct RectangularCoordinates {
296    /// X coordinate.
297    pub x: f64,
298    /// Y coordinate.
299    pub y: f64,
300    /// Z coordinate.
301    pub z: f64,
302}
303
304/// Structure representing spherical coordinates of a body.
305#[derive(Debug, Clone, Copy, PartialEq)]
306pub struct SphericalCoordinates {
307    lon: f64,
308    lat: f64,
309    dist: f64,
310}
311
312impl SphericalCoordinates {
313    /// Gets the ecliptic longitude of the body, in radians.
314    ///
315    /// This value represents the angular distance of an object along the ecliptic plane from the
316    /// primary direction. In the case of heliocentric coordinates, it represents the *l* parameter,
317    /// in geocentric coordinates, represents the *λ* parameter.
318    #[must_use]
319    pub fn longitude(&self) -> f64 {
320        self.lon
321    }
322
323    /// Gets the ecliptic latitude of the body, in radians.
324    ///
325    /// This value represents the angular distance of an object from the ecliptic towards the north
326    /// ecliptic pole. In the case of heliocentric coordinates, it represents the *b* parameter, in
327    /// geocentric coordinates, represents the *β* parameter.
328    #[must_use]
329    pub fn latitude(&self) -> f64 {
330        self.lat
331    }
332
333    /// Gets the distance to the center of mass, in astronomical units (*AU*).
334    ///
335    /// In the case of heliocentric coordinates, it represents the *r* parameter, in geocentric
336    /// coordinates, represents the *Δ* parameter.
337    #[must_use]
338    pub fn distance(&self) -> f64 {
339        self.dist
340    }
341}
342
343/// Calculates the time variable for VSOP87.
344#[inline]
345fn calculate_t(jde: f64) -> f64 {
346    (jde - 2_451_545_f64) / 365_250_f64
347}
348
349/// Calculates the given variable.
350#[inline]
351fn calculate_var(t: f64, a: &[f64], b: &[f64], c: &[f64]) -> f64 {
352    #[cfg(all(
353        any(target_arch = "x86", target_arch = "x86_64"),
354        feature = "simd",
355        not(feature = "no_std")
356    ))]
357    #[allow(unsafe_code)]
358    {
359        if is_x86_feature_detected!("avx") {
360            // Safe because we already checked that we have AVX instruction set.
361            unsafe { calculate_var_avx(t, a, b, c) }
362        } else {
363            calculate_var_fallback(t, a, b, c)
364        }
365    }
366
367    #[cfg(any(
368        all(not(target_arch = "x86"), not(target_arch = "x86_64")),
369        not(feature = "simd"),
370        feature = "no_std"
371    ))]
372    {
373        calculate_var_fallback(t, a, b, c)
374    }
375}
376
377/// Fallback implementation of the variable calculation.
378///
379/// Used in systems without SIMD support.
380#[inline]
381fn calculate_var_fallback(t: f64, a: &[f64], b: &[f64], c: &[f64]) -> f64 {
382    #[cfg(not(feature = "no_std"))]
383    {
384        a.iter()
385            .zip(b)
386            .zip(c)
387            .fold(0_f64, |term, ((a, b), c)| term + a * (b + c * t).cos())
388    }
389
390    #[cfg(feature = "no_std")]
391    {
392        a.iter()
393            .zip(b)
394            .zip(c)
395            .fold(0_f64, |term, ((a, b), c)| term + a * cos(b + c * t))
396    }
397}
398
399/// Calculates the given variable using the AVX instruction set.
400#[target_feature(enable = "avx")]
401#[cfg(all(
402    any(target_arch = "x86", target_arch = "x86_64"),
403    feature = "simd",
404    not(feature = "no_std")
405))]
406#[allow(unsafe_code)]
407unsafe fn calculate_var_avx(t: f64, a: &[f64], b: &[f64], c: &[f64]) -> f64 {
408    #[cfg(feature = "no_std")]
409    use core::{f64, mem};
410    #[cfg(not(feature = "no_std"))]
411    use std::{f64, mem};
412
413    #[cfg(all(feature = "no_std", target_arch = "x86_64"))]
414    use core::arch::x86_64::{_mm256_add_pd, _mm256_mul_pd, _mm256_set1_pd, _mm256_set_pd};
415    #[cfg(all(not(feature = "no_std"), target_arch = "x86_64"))]
416    use std::arch::x86_64::{_mm256_add_pd, _mm256_mul_pd, _mm256_set1_pd, _mm256_set_pd};
417
418    #[cfg(all(feature = "no_std", target_arch = "x86"))]
419    use core::arch::x86::{_mm256_add_pd, _mm256_mul_pd, _mm256_set1_pd, _mm256_set_pd};
420    #[cfg(all(not(feature = "no_std"), target_arch = "x86"))]
421    use std::arch::x86::{_mm256_add_pd, _mm256_mul_pd, _mm256_set1_pd, _mm256_set_pd};
422
423    /// Vectorizes the calculation of 4 terms at the same time.
424    unsafe fn vector_term(
425        (a1, b1, c1): (f64, f64, f64),
426        (a2, b2, c2): (f64, f64, f64),
427        (a3, b3, c3): (f64, f64, f64),
428        (a4, b4, c4): (f64, f64, f64),
429        t: f64,
430    ) -> (f64, f64, f64, f64) {
431        let a = _mm256_set_pd(a1, a2, a3, a4);
432        let b = _mm256_set_pd(b1, b2, b3, b4);
433        let c = _mm256_set_pd(c1, c2, c3, c4);
434        let t = _mm256_set1_pd(t);
435
436        // Safe because both values are created properly and checked.
437        let ct = _mm256_mul_pd(c, t);
438        // Safe because both values are created properly and checked.
439        let bct = _mm256_add_pd(b, ct);
440
441        // Safe because bct_unpacked is 4 f64 long.
442        let bct_unpacked: (f64, f64, f64, f64) = mem::transmute(bct);
443
444        // Safe because bct_unpacked is 4 f64 long, and x84/x86_64 is little endian.
445        let bct = _mm256_set_pd(
446            bct_unpacked.3.cos(),
447            bct_unpacked.2.cos(),
448            bct_unpacked.1.cos(),
449            bct_unpacked.0.cos(),
450        );
451
452        // Safe because both values are created properly and checked.
453        let term = _mm256_mul_pd(a, bct);
454        let term_unpacked: (f64, f64, f64, f64) = mem::transmute(term);
455
456        term_unpacked
457    }
458
459    let iter1 = a.chunks_exact(4);
460    let iter2 = b.chunks_exact(4);
461    let iter3 = c.chunks_exact(4);
462
463    let remainder = match (iter1.remainder(), iter2.remainder(), iter3.remainder()) {
464        (&[a1, a2, a3], &[b1, b2, b3], &[c1, c2, c3]) => {
465            // The result is little endian in x86/x86_64.
466            let (_term4, term3, term2, term1) = vector_term(
467                (a1, b1, c1),
468                (a2, b2, c2),
469                (a3, b3, c3),
470                (f64::NAN, f64::NAN, f64::NAN),
471                t,
472            );
473
474            term1 + term2 + term3
475        }
476        (&[a1, a2], &[b1, b2], &[c1, c2]) => a1 * (b1 + c1 * t).cos() + a2 * (b2 + c2 * t).cos(),
477        (&[a], &[b], &[c]) => a * (b + c * t).cos(),
478        (&[], &[], &[]) => 0_f64,
479        _ => unreachable!(),
480    };
481
482    let res = iter1
483        .zip(iter2)
484        .zip(iter3)
485        .map(|vars| match vars {
486            ((&[a1, a2, a3, a4], &[b1, b2, b3, b4]), &[c1, c2, c3, c4]) => {
487                // The result is little endian in x86/x86_64.
488                let (term4, term3, term2, term1) =
489                    vector_term((a1, b1, c1), (a2, b2, c2), (a3, b3, c3), (a4, b4, c4), t);
490
491                term1 + term2 + term3 + term4
492            }
493            _ => unreachable!(),
494        })
495        .sum::<f64>();
496
497    res + remainder
498}
499
500/// Elements used by the VSOP87 solution. Can be converted into keplerian elements.
501///
502/// More information can be found [here](http://totaleclipse.eu/Astronomy/VSOP87.html).
503#[derive(Debug, Clone, Copy, PartialEq)]
504pub struct VSOP87Elements {
505    /// Semimajor axis in astronomical units (*AU*).
506    pub a: f64,
507    /// Mean longitude at epoch.
508    pub l: f64,
509    /// `e * lper.cos()``, where *e* is the eccentricity and *lper* is the longitude of the
510    /// perihelion.
511    pub k: f64,
512    /// `e * lper.sin()``, where *e* is the eccentricity and *lper* is the longitude of the
513    /// perihelion (*ϖ*).
514    pub h: f64,
515    /// `(i/2.0).sin() * lan.cos()` where *i* is inclination and *lan is the longitude of the
516    /// ascending node (*Ω*).
517    pub q: f64,
518    /// `(i/2.0).sin() * lan.sin()` where *i* is inclination and *lan is the longitude of the
519    /// ascending node (*Ω*).
520    pub p: f64,
521}
522
523impl From<KeplerianElements> for VSOP87Elements {
524    #[inline]
525    fn from(elts: KeplerianElements) -> Self {
526        #[cfg(feature = "no_std")]
527        {
528            let (lper_sin, lper_cos) = sincos(elts.lper);
529            let (lan_sin, lan_cos) = sincos(elts.lan);
530            let incl_sin = sin(elts.incl / 2.0);
531            Self {
532                a: elts.sma,
533                l: elts.l0,
534                k: elts.ecc * lper_cos,
535                h: elts.ecc * lper_sin,
536                q: incl_sin * lan_cos,
537                p: incl_sin * lan_sin,
538            }
539        }
540
541        #[cfg(not(feature = "no_std"))]
542        {
543            let (lper_sin, lper_cos) = elts.lper.sin_cos();
544            let (lan_sin, lan_cos) = elts.lan.sin_cos();
545            let incl_sin = (elts.incl / 2.0).sin();
546            Self {
547                a: elts.sma,
548                l: elts.l0,
549                k: elts.ecc * lper_cos,
550                h: elts.ecc * lper_sin,
551                q: incl_sin * lan_cos,
552                p: incl_sin * lan_sin,
553            }
554        }
555    }
556}
557
558/// Calculates VSOP87 solution for Mercury.
559///
560/// This function calculates the VSOP87 solution (heliocentric ecliptic orbital elements for the
561/// equinox J2000.0) for the planet Mercury. The parameter needed is the Julian Day (*JD*) for the
562/// given date. It returns the `VSOP87Elements` of the VSOP87 solution.
563///
564/// # Example
565///
566/// Given a date in [*JD*](http://aa.usno.navy.mil/data/docs/JulianDate.php), we can get the
567/// orbit of the planet Mercury. In this case, we calculate the orbit of Mercury in December 31st,
568/// 1899.
569///
570/// ```
571/// let vsop87_elts = vsop87::mercury(2415020.0);
572///
573/// assert!(vsop87_elts.a > 0.3870977205 && vsop87_elts.a < 0.3870977207);
574/// assert!(vsop87_elts.l > 3.1341564064 && vsop87_elts.l < 3.1341564066);
575/// assert!(vsop87_elts.k > 0.0452159417 && vsop87_elts.k < 0.0452159419);
576/// assert!(vsop87_elts.h > 0.2005915793 && vsop87_elts.h < 0.2005915795);
577/// assert!(vsop87_elts.q > 0.0405500077 && vsop87_elts.q < 0.0405500079);
578/// assert!(vsop87_elts.p > 0.04576328 && vsop87_elts.p < 0.04576404);
579/// ```
580///
581/// It can then be converted into keplerian elements:
582///
583/// ```
584/// use vsop87::{KeplerianElements, VSOP87Elements};
585///
586/// # let vsop87_elts = vsop87::mercury(2415020.0);
587/// #
588/// let k_elements: KeplerianElements = vsop87_elts.into();
589/// let convert_back = VSOP87Elements::from(k_elements);
590/// ```
591#[must_use]
592pub fn mercury(jde: f64) -> VSOP87Elements {
593    let t = calculate_t(jde);
594
595    let a0 = calculate_var(t, &mercury::A0[0], &mercury::A0[1], &mercury::A0[2]);
596    let a1 = calculate_var(t, &mercury::A1[0], &mercury::A1[1], &mercury::A1[2]);
597    let a2 = calculate_var(t, &mercury::A2[0], &mercury::A2[1], &mercury::A2[2]);
598
599    let l0 = calculate_var(t, &mercury::L0[0], &mercury::L0[1], &mercury::L0[2]);
600    let l1 = calculate_var(t, &mercury::L1[0], &mercury::L1[1], &mercury::L1[2]);
601    let l2 = calculate_var(t, &mercury::L2[0], &mercury::L2[1], &mercury::L2[2]);
602    let l3 = calculate_var(t, &mercury::L3[0], &mercury::L3[1], &mercury::L3[2]);
603
604    let k0 = calculate_var(t, &mercury::K0[0], &mercury::K0[1], &mercury::K0[2]);
605    let k1 = calculate_var(t, &mercury::K1[0], &mercury::K1[1], &mercury::K1[2]);
606    let k2 = calculate_var(t, &mercury::K2[0], &mercury::K2[1], &mercury::K2[2]);
607    let k3 = calculate_var(t, &mercury::K3[0], &mercury::K3[1], &mercury::K3[2]);
608    let k4 = calculate_var(t, &mercury::K4[0], &mercury::K4[1], &mercury::K4[2]);
609    let k5 = calculate_var(t, &mercury::K5[0], &mercury::K5[1], &mercury::K5[2]);
610
611    let h0 = calculate_var(t, &mercury::H0[0], &mercury::H0[1], &mercury::H0[2]);
612    let h1 = calculate_var(t, &mercury::H1[0], &mercury::H1[1], &mercury::H1[2]);
613    let h2 = calculate_var(t, &mercury::H2[0], &mercury::H2[1], &mercury::H2[2]);
614    let h3 = calculate_var(t, &mercury::H3[0], &mercury::H3[1], &mercury::H3[2]);
615    let h4 = calculate_var(t, &mercury::H4[0], &mercury::H4[1], &mercury::H4[2]);
616    let h5 = calculate_var(t, &mercury::H5[0], &mercury::H5[1], &mercury::H5[2]);
617
618    let q0 = calculate_var(t, &mercury::Q0[0], &mercury::Q0[1], &mercury::Q0[2]);
619    let q1 = calculate_var(t, &mercury::Q1[0], &mercury::Q1[1], &mercury::Q1[2]);
620    let q2 = calculate_var(t, &mercury::Q2[0], &mercury::Q2[1], &mercury::Q2[2]);
621    let q3 = calculate_var(t, &mercury::Q3[0], &mercury::Q3[1], &mercury::Q3[2]);
622    let q4 = calculate_var(t, &mercury::Q4[0], &mercury::Q4[1], &mercury::Q4[2]);
623    let q5 = calculate_var(t, &mercury::Q5[0], &mercury::Q5[1], &mercury::Q5[2]);
624
625    let p0 = calculate_var(t, &mercury::P0[0], &mercury::P0[1], &mercury::P0[2]);
626    let p1 = calculate_var(t, &mercury::P1[0], &mercury::P1[1], &mercury::P1[2]);
627    let p2 = calculate_var(t, &mercury::P2[0], &mercury::P2[1], &mercury::P2[2]);
628    let p3 = calculate_var(t, &mercury::P3[0], &mercury::P3[1], &mercury::P3[2]);
629    let p4 = calculate_var(t, &mercury::P4[0], &mercury::P4[1], &mercury::P4[2]);
630
631    // We calculate the `t` potencies beforehand for easy re-use.
632    let t2 = t * t;
633    let t3 = t2 * t;
634    let t4 = t2 * t2;
635    let t5 = t2 * t3;
636
637    let a = a0 + a1 * t + a2 * t2;
638    let l = (l0 + l1 * t + l2 * t2 + l3 * t3) % (2_f64 * PI);
639    let k = k0 + k1 * t + k2 * t2 + k3 * t3 + k4 * t4 + k5 * t5;
640    let h = h0 + h1 * t + h2 * t2 + h3 * t3 + h4 * t4 + h5 * t5;
641    let q = q0 + q1 * t + q2 * t2 + q3 * t3 + q4 * t4 + q5 * t5;
642    let p = p0 + p1 * t + p2 * t2 + p3 * t3 + p4 * t4;
643
644    VSOP87Elements {
645        a,
646        l: if l > 0_f64 { l } else { 2_f64 * PI + l },
647        k,
648        h,
649        q,
650        p,
651    }
652}
653
654/// Calculates VSOP87 solution for Venus.
655///
656/// This function calculates the VSOP87 solution (heliocentric ecliptic orbital elements for the
657/// equinox J2000.0) for the planet Venus. The parameter needed is the Julian Day (*JD*) for the
658/// given date. It returns the `VSOP87Elements` of the VSOP87 solution.
659///
660/// # Example
661///
662/// Given a date in [*JD*](http://aa.usno.navy.mil/data/docs/JulianDate.php), we can get the
663/// orbit of the planet Venus. In this case, we calculate the orbit of Venus in January 1st, 2000.
664///
665/// ```
666/// let vsop87_elts = vsop87::venus(2451545.0);
667///
668/// assert!(vsop87_elts.a > 0.7233269303 && vsop87_elts.a < 0.7233269305);
669/// assert!(vsop87_elts.l > 3.1761350909 && vsop87_elts.l < 3.1761350911);
670/// assert!(vsop87_elts.k > -0.0045086078 && vsop87_elts.k < -0.0045086076);
671/// assert!(vsop87_elts.h > 0.0050312181 && vsop87_elts.h < 0.0050312183);
672/// assert!(vsop87_elts.q > 0.0068248057 && vsop87_elts.q < 0.0068248059);
673/// assert!(vsop87_elts.p > 0.02882177 && vsop87_elts.p < 0.02882253);
674/// ```
675///
676/// It can then be converted into keplerian elements:
677///
678/// ```
679/// use vsop87::{KeplerianElements, VSOP87Elements};
680///
681/// # let vsop87_elts = vsop87::venus(2451545.0);
682/// #
683/// let k_elements: KeplerianElements = vsop87_elts.into();
684/// let convert_back = VSOP87Elements::from(k_elements);
685/// ```
686#[must_use]
687pub fn venus(jde: f64) -> VSOP87Elements {
688    let t = calculate_t(jde);
689
690    let a0 = calculate_var(t, &venus::A0[0], &venus::A0[1], &venus::A0[2]);
691    let a1 = calculate_var(t, &venus::A1[0], &venus::A1[1], &venus::A1[2]);
692    let a2 = calculate_var(t, &venus::A2[0], &venus::A2[1], &venus::A2[2]);
693
694    let l0 = calculate_var(t, &venus::L0[0], &venus::L0[1], &venus::L0[2]);
695    let l1 = calculate_var(t, &venus::L1[0], &venus::L1[1], &venus::L1[2]);
696    let l2 = calculate_var(t, &venus::L2[0], &venus::L2[1], &venus::L2[2]);
697    let l3 = calculate_var(t, &venus::L3[0], &venus::L3[1], &venus::L3[2]);
698
699    let k0 = calculate_var(t, &venus::K0[0], &venus::K0[1], &venus::K0[2]);
700    let k1 = calculate_var(t, &venus::K1[0], &venus::K1[1], &venus::K1[2]);
701    let k2 = calculate_var(t, &venus::K2[0], &venus::K2[1], &venus::K2[2]);
702    let k3 = calculate_var(t, &venus::K3[0], &venus::K3[1], &venus::K3[2]);
703    let k4 = calculate_var(t, &venus::K4[0], &venus::K4[1], &venus::K4[2]);
704    let k5 = calculate_var(t, &venus::K5[0], &venus::K5[1], &venus::K5[2]);
705
706    let h0 = calculate_var(t, &venus::H0[0], &venus::H0[1], &venus::H0[2]);
707    let h1 = calculate_var(t, &venus::H1[0], &venus::H1[1], &venus::H1[2]);
708    let h2 = calculate_var(t, &venus::H2[0], &venus::H2[1], &venus::H2[2]);
709    let h3 = calculate_var(t, &venus::H3[0], &venus::H3[1], &venus::H3[2]);
710    let h4 = calculate_var(t, &venus::H4[0], &venus::H4[1], &venus::H4[2]);
711    let h5 = calculate_var(t, &venus::H5[0], &venus::H5[1], &venus::H5[2]);
712
713    let q0 = calculate_var(t, &venus::Q0[0], &venus::Q0[1], &venus::Q0[2]);
714    let q1 = calculate_var(t, &venus::Q1[0], &venus::Q1[1], &venus::Q1[2]);
715    let q2 = calculate_var(t, &venus::Q2[0], &venus::Q2[1], &venus::Q2[2]);
716    let q3 = calculate_var(t, &venus::Q3[0], &venus::Q3[1], &venus::Q3[2]);
717    let q4 = calculate_var(t, &venus::Q4[0], &venus::Q4[1], &venus::Q4[2]);
718    let q5 = calculate_var(t, &venus::Q5[0], &venus::Q5[1], &venus::Q5[2]);
719
720    let p0 = calculate_var(t, &venus::P0[0], &venus::P0[1], &venus::P0[2]);
721    let p1 = calculate_var(t, &venus::P1[0], &venus::P1[1], &venus::P1[2]);
722    let p2 = calculate_var(t, &venus::P2[0], &venus::P2[1], &venus::P2[2]);
723    let p3 = calculate_var(t, &venus::P3[0], &venus::P3[1], &venus::P3[2]);
724    let p4 = calculate_var(t, &venus::P4[0], &venus::P4[1], &venus::P4[2]);
725
726    // We calculate the `t` potencies beforehand for easy re-use.
727    let t2 = t * t;
728    let t3 = t2 * t;
729    let t4 = t2 * t2;
730    let t5 = t2 * t3;
731
732    let a = a0 + a1 * t + a2 * t2;
733    let l = (l0 + l1 * t + l2 * t2 + l3 * t3) % (2_f64 * PI);
734    let k = k0 + k1 * t + k2 * t2 + k3 * t3 + k4 * t4 + k5 * t5;
735    let h = h0 + h1 * t + h2 * t2 + h3 * t3 + h4 * t4 + h5 * t5;
736    let q = q0 + q1 * t + q2 * t2 + q3 * t3 + q4 * t4 + q5 * t5;
737    let p = p0 + p1 * t + p2 * t2 + p3 * t3 + p4 * t4;
738
739    VSOP87Elements {
740        a,
741        l: if l > 0_f64 { l } else { 2_f64 * PI + l },
742        k,
743        h,
744        q,
745        p,
746    }
747}
748
749/// Calculates VSOP87 solution for Earth - Moon barycenter.
750///
751/// This function calculates the VSOP87 solution (heliocentric ecliptic orbital elements for the
752/// equinox J2000.0) for the Earth - Moon barycenter. The parameter needed is the Julian Day (*JD*)
753/// for the given date. It returns the `VSOP87Elements` of the VSOP87 solution.
754///
755/// # Example
756///
757/// Given a date in [*JD*](http://aa.usno.navy.mil/data/docs/JulianDate.php), we can get the
758/// orbit of the Earth - Moon barycenter (the center of masses between the Earth and the Moon, not
759/// exactly the center of the Earth). In this case, we calculate the orbit of the Earth - Moon
760/// barycenter in December 19th, 1099.
761/// ```
762/// let vsop87_elts = vsop87::earth_moon(2122820.0);
763///
764/// assert!(vsop87_elts.a > 1.0000134925 && vsop87_elts.a < 1.0000134927);
765/// assert!(vsop87_elts.l > 1.8519621672 && vsop87_elts.l < 1.8519621674);
766/// assert!(vsop87_elts.k > -0.0029638176 && vsop87_elts.k < -0.0029638174);
767/// assert!(vsop87_elts.h > 0.0168402193 && vsop87_elts.h < 0.0168402195);
768/// assert!(vsop87_elts.q > 0.0010301900 && vsop87_elts.q < 0.0010301902);
769/// assert!(vsop87_elts.p > -0.00005346 && vsop87_elts.p < -0.00005270);
770/// ```
771///
772/// It can then be converted into keplerian elements:
773///
774/// ```
775/// use vsop87::{KeplerianElements, VSOP87Elements};
776///
777/// # let vsop87_elts = vsop87::earth_moon(2122820.0);
778/// #
779/// let k_elements: KeplerianElements = vsop87_elts.into();
780/// let convert_back = VSOP87Elements::from(k_elements);
781/// ```
782#[allow(clippy::too_many_lines)]
783#[must_use]
784pub fn earth_moon(jde: f64) -> VSOP87Elements {
785    let t = calculate_t(jde);
786
787    let a0 = calculate_var(
788        t,
789        &earth_moon::A0[0],
790        &earth_moon::A0[1],
791        &earth_moon::A0[2],
792    );
793    let a1 = calculate_var(
794        t,
795        &earth_moon::A1[0],
796        &earth_moon::A1[1],
797        &earth_moon::A1[2],
798    );
799    let a2 = calculate_var(
800        t,
801        &earth_moon::A2[0],
802        &earth_moon::A2[1],
803        &earth_moon::A2[2],
804    );
805
806    let l0 = calculate_var(
807        t,
808        &earth_moon::L0[0],
809        &earth_moon::L0[1],
810        &earth_moon::L0[2],
811    );
812    let l1 = calculate_var(
813        t,
814        &earth_moon::L1[0],
815        &earth_moon::L1[1],
816        &earth_moon::L1[2],
817    );
818    let l2 = calculate_var(
819        t,
820        &earth_moon::L2[0],
821        &earth_moon::L2[1],
822        &earth_moon::L2[2],
823    );
824    let l3 = calculate_var(
825        t,
826        &earth_moon::L3[0],
827        &earth_moon::L3[1],
828        &earth_moon::L3[2],
829    );
830    let l4 = calculate_var(
831        t,
832        &earth_moon::L4[0],
833        &earth_moon::L4[1],
834        &earth_moon::L4[2],
835    );
836    let l5 = calculate_var(
837        t,
838        &earth_moon::L5[0],
839        &earth_moon::L5[1],
840        &earth_moon::L5[2],
841    );
842
843    let k0 = calculate_var(
844        t,
845        &earth_moon::K0[0],
846        &earth_moon::K0[1],
847        &earth_moon::K0[2],
848    );
849    let k1 = calculate_var(
850        t,
851        &earth_moon::K1[0],
852        &earth_moon::K1[1],
853        &earth_moon::K1[2],
854    );
855    let k2 = calculate_var(
856        t,
857        &earth_moon::K2[0],
858        &earth_moon::K2[1],
859        &earth_moon::K2[2],
860    );
861    let k3 = calculate_var(
862        t,
863        &earth_moon::K3[0],
864        &earth_moon::K3[1],
865        &earth_moon::K3[2],
866    );
867    let k4 = calculate_var(
868        t,
869        &earth_moon::K4[0],
870        &earth_moon::K4[1],
871        &earth_moon::K4[2],
872    );
873    let k5 = calculate_var(
874        t,
875        &earth_moon::K5[0],
876        &earth_moon::K5[1],
877        &earth_moon::K5[2],
878    );
879
880    let h0 = calculate_var(
881        t,
882        &earth_moon::H0[0],
883        &earth_moon::H0[1],
884        &earth_moon::H0[2],
885    );
886    let h1 = calculate_var(
887        t,
888        &earth_moon::H1[0],
889        &earth_moon::H1[1],
890        &earth_moon::H1[2],
891    );
892    let h2 = calculate_var(
893        t,
894        &earth_moon::H2[0],
895        &earth_moon::H2[1],
896        &earth_moon::H2[2],
897    );
898    let h3 = calculate_var(
899        t,
900        &earth_moon::H3[0],
901        &earth_moon::H3[1],
902        &earth_moon::H3[2],
903    );
904    let h4 = calculate_var(
905        t,
906        &earth_moon::H4[0],
907        &earth_moon::H4[1],
908        &earth_moon::H4[2],
909    );
910    let h5 = calculate_var(
911        t,
912        &earth_moon::H5[0],
913        &earth_moon::H5[1],
914        &earth_moon::H5[2],
915    );
916
917    let q0 = calculate_var(
918        t,
919        &earth_moon::Q0[0],
920        &earth_moon::Q0[1],
921        &earth_moon::Q0[2],
922    );
923    let q1 = calculate_var(
924        t,
925        &earth_moon::Q1[0],
926        &earth_moon::Q1[1],
927        &earth_moon::Q1[2],
928    );
929    let q2 = calculate_var(
930        t,
931        &earth_moon::Q2[0],
932        &earth_moon::Q2[1],
933        &earth_moon::Q2[2],
934    );
935    let q3 = calculate_var(
936        t,
937        &earth_moon::Q3[0],
938        &earth_moon::Q3[1],
939        &earth_moon::Q3[2],
940    );
941    let q4 = calculate_var(
942        t,
943        &earth_moon::Q4[0],
944        &earth_moon::Q4[1],
945        &earth_moon::Q4[2],
946    );
947    let q5 = calculate_var(
948        t,
949        &earth_moon::Q5[0],
950        &earth_moon::Q5[1],
951        &earth_moon::Q5[2],
952    );
953
954    let p0 = calculate_var(
955        t,
956        &earth_moon::P0[0],
957        &earth_moon::P0[1],
958        &earth_moon::P0[2],
959    );
960    let p1 = calculate_var(
961        t,
962        &earth_moon::P1[0],
963        &earth_moon::P1[1],
964        &earth_moon::P1[2],
965    );
966    let p2 = calculate_var(
967        t,
968        &earth_moon::P2[0],
969        &earth_moon::P2[1],
970        &earth_moon::P2[2],
971    );
972    let p3 = calculate_var(
973        t,
974        &earth_moon::P3[0],
975        &earth_moon::P3[1],
976        &earth_moon::P3[2],
977    );
978    let p4 = calculate_var(
979        t,
980        &earth_moon::P4[0],
981        &earth_moon::P4[1],
982        &earth_moon::P4[2],
983    );
984
985    // We calculate the `t` potencies beforehand for easy re-use.
986    let t2 = t * t;
987    let t3 = t2 * t;
988    let t4 = t2 * t2;
989    let t5 = t2 * t3;
990
991    let a = a0 + a1 * t + a2 * t2;
992    let l = (l0 + l1 * t + l2 * t2 + l3 * t3 + l4 * t4 + l5 * t5) % (2_f64 * PI);
993    let k = k0 + k1 * t + k2 * t2 + k3 * t3 + k4 * t4 + k5 * t5;
994    let h = h0 + h1 * t + h2 * t2 + h3 * t3 + h4 * t4 + h5 * t5;
995    let q = q0 + q1 * t + q2 * t2 + q3 * t3 + q4 * t4 + q5 * t5;
996    let p = p0 + p1 * t + p2 * t2 + p3 * t3 + p4 * t4;
997
998    VSOP87Elements {
999        a,
1000        l: if l > 0_f64 { l } else { 2_f64 * PI + l },
1001        k,
1002        h,
1003        q,
1004        p,
1005    }
1006}
1007
1008/// Calculates VSOP87 solution for Mars.
1009///
1010/// This function calculates the VSOP87 solution (heliocentric ecliptic orbital elements for the
1011/// equinox J2000.0) for the planet Mars. The parameter needed is the Julian Day (*JD*) for the
1012/// given date. It returns the `VSOP87Elements` of the VSOP87 solution.
1013///
1014/// # Example
1015///
1016/// Given a date in [*JD*](http://aa.usno.navy.mil/data/docs/JulianDate.php), we can get the
1017/// orbit of the planet Mars. In this case, we calculate the orbit of Mars in December 19th, 1199.
1018///
1019/// ```
1020/// let vsop87_elts = vsop87::mars(2159345.0);
1021///
1022/// assert!(vsop87_elts.a > 1.5237578877 && vsop87_elts.a < 1.5237578879);
1023/// assert!(vsop87_elts.l > 4.0669853278 && vsop87_elts.l < 4.0669853280);
1024/// assert!(vsop87_elts.k > 0.0821906316 && vsop87_elts.k < 0.0821906318);
1025/// assert!(vsop87_elts.h > -0.0427917583 && vsop87_elts.h < -0.0427917581);
1026/// assert!(vsop87_elts.q > 0.0103081045 && vsop87_elts.q < 0.0103081047);
1027/// assert!(vsop87_elts.p > 0.01313608 && vsop87_elts.p < 0.01313684);
1028/// ```
1029///
1030/// It can then be converted into keplerian elements:
1031///
1032/// ```
1033/// use vsop87::{KeplerianElements, VSOP87Elements};
1034///
1035/// # let vsop87_elts = vsop87::mars(2159345.0);
1036/// #
1037/// let k_elements: KeplerianElements = vsop87_elts.into();
1038/// let convert_back = VSOP87Elements::from(k_elements);
1039/// ```
1040#[must_use]
1041pub fn mars(jde: f64) -> VSOP87Elements {
1042    let t = calculate_t(jde);
1043
1044    let a0 = calculate_var(t, &mars::A0[0], &mars::A0[1], &mars::A0[2]);
1045    let a1 = calculate_var(t, &mars::A1[0], &mars::A1[1], &mars::A1[2]);
1046    let a2 = calculate_var(t, &mars::A2[0], &mars::A2[1], &mars::A2[2]);
1047
1048    let l0 = calculate_var(t, &mars::L0[0], &mars::L0[1], &mars::L0[2]);
1049    let l1 = calculate_var(t, &mars::L1[0], &mars::L1[1], &mars::L1[2]);
1050    let l2 = calculate_var(t, &mars::L2[0], &mars::L2[1], &mars::L2[2]);
1051    let l3 = calculate_var(t, &mars::L3[0], &mars::L3[1], &mars::L3[2]);
1052    let l4 = calculate_var(t, &mars::L4[0], &mars::L4[1], &mars::L4[2]);
1053    let l5 = calculate_var(t, &mars::L5[0], &mars::L5[1], &mars::L5[2]);
1054
1055    let k0 = calculate_var(t, &mars::K0[0], &mars::K0[1], &mars::K0[2]);
1056    let k1 = calculate_var(t, &mars::K1[0], &mars::K1[1], &mars::K1[2]);
1057    let k2 = calculate_var(t, &mars::K2[0], &mars::K2[1], &mars::K2[2]);
1058    let k3 = calculate_var(t, &mars::K3[0], &mars::K3[1], &mars::K3[2]);
1059    let k4 = calculate_var(t, &mars::K4[0], &mars::K4[1], &mars::K4[2]);
1060    let k5 = calculate_var(t, &mars::K5[0], &mars::K5[1], &mars::K5[2]);
1061
1062    let h0 = calculate_var(t, &mars::H0[0], &mars::H0[1], &mars::H0[2]);
1063    let h1 = calculate_var(t, &mars::H1[0], &mars::H1[1], &mars::H1[2]);
1064    let h2 = calculate_var(t, &mars::H2[0], &mars::H2[1], &mars::H2[2]);
1065    let h3 = calculate_var(t, &mars::H3[0], &mars::H3[1], &mars::H3[2]);
1066    let h4 = calculate_var(t, &mars::H4[0], &mars::H4[1], &mars::H4[2]);
1067    let h5 = calculate_var(t, &mars::H5[0], &mars::H5[1], &mars::H5[2]);
1068
1069    let q0 = calculate_var(t, &mars::Q0[0], &mars::Q0[1], &mars::Q0[2]);
1070    let q1 = calculate_var(t, &mars::Q1[0], &mars::Q1[1], &mars::Q1[2]);
1071    let q2 = calculate_var(t, &mars::Q2[0], &mars::Q2[1], &mars::Q2[2]);
1072    let q3 = calculate_var(t, &mars::Q3[0], &mars::Q3[1], &mars::Q3[2]);
1073    let q4 = calculate_var(t, &mars::Q4[0], &mars::Q4[1], &mars::Q4[2]);
1074    let q5 = calculate_var(t, &mars::Q5[0], &mars::Q5[1], &mars::Q5[2]);
1075
1076    let p0 = calculate_var(t, &mars::P0[0], &mars::P0[1], &mars::P0[2]);
1077    let p1 = calculate_var(t, &mars::P1[0], &mars::P1[1], &mars::P1[2]);
1078    let p2 = calculate_var(t, &mars::P2[0], &mars::P2[1], &mars::P2[2]);
1079    let p3 = calculate_var(t, &mars::P3[0], &mars::P3[1], &mars::P3[2]);
1080
1081    // We calculate the `t` potencies beforehand for easy re-use.
1082    let t2 = t * t;
1083    let t3 = t2 * t;
1084    let t4 = t2 * t2;
1085    let t5 = t2 * t3;
1086
1087    let a = a0 + a1 * t + a2 * t2;
1088    let l = (l0 + l1 * t + l2 * t2 + l3 * t3 + l4 * t4 + l5 * t5) % (2_f64 * PI);
1089    let k = k0 + k1 * t + k2 * t2 + k3 * t3 + k4 * t4 + k5 * t5;
1090    let h = h0 + h1 * t + h2 * t2 + h3 * t3 + h4 * t4 + h5 * t5;
1091    let q = q0 + q1 * t + q2 * t2 + q3 * t3 + q4 * t4 + q5 * t5;
1092    let p = p0 + p1 * t + p2 * t2 + p3 * t3;
1093
1094    VSOP87Elements {
1095        a,
1096        l: if l > 0_f64 { l } else { 2_f64 * PI + l },
1097        k,
1098        h,
1099        q,
1100        p,
1101    }
1102}
1103
1104/// Calculates VSOP87 solution for Jupiter.
1105///
1106/// This function calculates the VSOP87 solution (heliocentric ecliptic orbital elements for the
1107/// equinox J2000.0) for the planet Jupiter. The parameter needed is the Julian Day (*JD*) for the
1108/// given date. It returns the `VSOP87Elements` of the VSOP87 solution.
1109///
1110/// # Example
1111///
1112/// Given a date in [*JD*](http://aa.usno.navy.mil/data/docs/JulianDate.php), we can get the
1113/// orbit of the planet Jupiter. In this case, we calculate the orbit of Jupiter in December 30th,
1114/// 1799.
1115///
1116/// ```
1117/// let vsop87_elts = vsop87::jupiter(2378495.0);
1118///
1119/// assert!(vsop87_elts.a > 5.2027276672 && vsop87_elts.a < 5.2027276674);
1120/// assert!(vsop87_elts.l > 1.4820596291 && vsop87_elts.l < 1.4820596293);
1121/// assert!(vsop87_elts.k > 0.0464780412 && vsop87_elts.k < 0.0464780414);
1122/// assert!(vsop87_elts.h > 0.0116460263 && vsop87_elts.h < 0.0116460265);
1123/// assert!(vsop87_elts.q > -0.0019921307 && vsop87_elts.q < -0.0019921305);
1124/// assert!(vsop87_elts.p > 0.01123447 && vsop87_elts.p < 0.01123523);
1125/// ```
1126///
1127/// It can then be converted into keplerian elements:
1128///
1129/// ```
1130/// use vsop87::{KeplerianElements, VSOP87Elements};
1131///
1132/// # let vsop87_elts = vsop87::jupiter(2378495.0);
1133/// #
1134/// let k_elements: KeplerianElements = vsop87_elts.into();
1135/// let convert_back = VSOP87Elements::from(k_elements);
1136/// ```
1137#[must_use]
1138pub fn jupiter(jde: f64) -> VSOP87Elements {
1139    let t = calculate_t(jde);
1140
1141    let a0 = calculate_var(t, &jupiter::A0[0], &jupiter::A0[1], &jupiter::A0[2]);
1142    let a1 = calculate_var(t, &jupiter::A1[0], &jupiter::A1[1], &jupiter::A1[2]);
1143    let a2 = calculate_var(t, &jupiter::A2[0], &jupiter::A2[1], &jupiter::A2[2]);
1144    let a3 = calculate_var(t, &jupiter::A3[0], &jupiter::A3[1], &jupiter::A3[2]);
1145    let a4 = calculate_var(t, &jupiter::A4[0], &jupiter::A4[1], &jupiter::A4[2]);
1146    let a5 = calculate_var(t, &jupiter::A5[0], &jupiter::A5[1], &jupiter::A5[2]);
1147
1148    let l0 = calculate_var(t, &jupiter::L0[0], &jupiter::L0[1], &jupiter::L0[2]);
1149    let l1 = calculate_var(t, &jupiter::L1[0], &jupiter::L1[1], &jupiter::L1[2]);
1150    let l2 = calculate_var(t, &jupiter::L2[0], &jupiter::L2[1], &jupiter::L2[2]);
1151    let l3 = calculate_var(t, &jupiter::L3[0], &jupiter::L3[1], &jupiter::L3[2]);
1152    let l4 = calculate_var(t, &jupiter::L4[0], &jupiter::L4[1], &jupiter::L4[2]);
1153    let l5 = calculate_var(t, &jupiter::L5[0], &jupiter::L5[1], &jupiter::L5[2]);
1154
1155    let k0 = calculate_var(t, &jupiter::K0[0], &jupiter::K0[1], &jupiter::K0[2]);
1156    let k1 = calculate_var(t, &jupiter::K1[0], &jupiter::K1[1], &jupiter::K1[2]);
1157    let k2 = calculate_var(t, &jupiter::K2[0], &jupiter::K2[1], &jupiter::K2[2]);
1158    let k3 = calculate_var(t, &jupiter::K3[0], &jupiter::K3[1], &jupiter::K3[2]);
1159    let k4 = calculate_var(t, &jupiter::K4[0], &jupiter::K4[1], &jupiter::K4[2]);
1160
1161    let h0 = calculate_var(t, &jupiter::H0[0], &jupiter::H0[1], &jupiter::H0[2]);
1162    let h1 = calculate_var(t, &jupiter::H1[0], &jupiter::H1[1], &jupiter::H1[2]);
1163    let h2 = calculate_var(t, &jupiter::H2[0], &jupiter::H2[1], &jupiter::H2[2]);
1164    let h3 = calculate_var(t, &jupiter::H3[0], &jupiter::H3[1], &jupiter::H3[2]);
1165    let h4 = calculate_var(t, &jupiter::H4[0], &jupiter::H4[1], &jupiter::H4[2]);
1166
1167    let q0 = calculate_var(t, &jupiter::Q0[0], &jupiter::Q0[1], &jupiter::Q0[2]);
1168    let q1 = calculate_var(t, &jupiter::Q1[0], &jupiter::Q1[1], &jupiter::Q1[2]);
1169    let q2 = calculate_var(t, &jupiter::Q2[0], &jupiter::Q2[1], &jupiter::Q2[2]);
1170    let q3 = calculate_var(t, &jupiter::Q3[0], &jupiter::Q3[1], &jupiter::Q3[2]);
1171
1172    let p0 = calculate_var(t, &jupiter::P0[0], &jupiter::P0[1], &jupiter::P0[2]);
1173    let p1 = calculate_var(t, &jupiter::P1[0], &jupiter::P1[1], &jupiter::P1[2]);
1174    let p2 = calculate_var(t, &jupiter::P2[0], &jupiter::P2[1], &jupiter::P2[2]);
1175
1176    // We calculate the `t` potencies beforehand for easy re-use.
1177    let t2 = t * t;
1178    let t3 = t2 * t;
1179    let t4 = t2 * t2;
1180    let t5 = t2 * t3;
1181
1182    let a = a0 + a1 * t + a2 * t2 + a3 * t3 + a4 * t4 + a5 * t5;
1183    let l = (l0 + l1 * t + l2 * t2 + l3 * t3 + l4 * t4 + l5 * t5) % (2_f64 * PI);
1184    let k = k0 + k1 * t + k2 * t2 + k3 * t3 + k4 * t4;
1185    let h = h0 + h1 * t + h2 * t2 + h3 * t3 + h4 * t4;
1186    let q = q0 + q1 * t + q2 * t2 + q3 * t3;
1187    let p = p0 + p1 * t + p2 * t2;
1188
1189    VSOP87Elements {
1190        a,
1191        l: if l > 0_f64 { l } else { 2_f64 * PI + l },
1192        k,
1193        h,
1194        q,
1195        p,
1196    }
1197}
1198
1199/// Calculates VSOP87 solution for Saturn.
1200///
1201/// This function calculates the VSOP87 solution (heliocentric ecliptic orbital elements for the
1202/// equinox J2000.0) for the planet Saturn. The parameter needed is the Julian Day (*JD*) for the
1203/// given date. It returns the `VSOP87Elements` of the VSOP87 solution.
1204///
1205/// # Example
1206///
1207/// Given a date in [*JD*](http://aa.usno.navy.mil/data/docs/JulianDate.php), we can get the
1208/// orbit of the planet Saturn. In this case, we calculate the orbit of Saturn in December 29th,
1209/// 1599.
1210///
1211/// ```
1212/// let vsop87_elts = vsop87::saturn(2305445.0);
1213///
1214/// assert!(vsop87_elts.a > 9.5727100002 && vsop87_elts.a < 9.5727100004);
1215/// assert!(vsop87_elts.l > 3.5107821038 && vsop87_elts.l < 3.5107821040);
1216/// assert!(vsop87_elts.k > -0.0048218813 && vsop87_elts.k < -0.0048218811);
1217/// assert!(vsop87_elts.h > 0.0575514202 && vsop87_elts.h < 0.0575514204);
1218/// assert!(vsop87_elts.q > -0.0090348990 && vsop87_elts.q < -0.0090348988);
1219/// assert!(vsop87_elts.p > 0.01965756 && vsop87_elts.p < 0.01965832);
1220/// ```
1221///
1222/// It can then be converted into keplerian elements:
1223///
1224/// ```
1225/// use vsop87::{KeplerianElements, VSOP87Elements};
1226///
1227/// # let vsop87_elts = vsop87::saturn(2305445.0);
1228/// #
1229/// let k_elements: KeplerianElements = vsop87_elts.into();
1230/// let convert_back = VSOP87Elements::from(k_elements);
1231/// ```
1232#[must_use]
1233pub fn saturn(jde: f64) -> VSOP87Elements {
1234    let t = calculate_t(jde);
1235
1236    let a0 = calculate_var(t, &saturn::A0[0], &saturn::A0[1], &saturn::A0[2]);
1237    let a1 = calculate_var(t, &saturn::A1[0], &saturn::A1[1], &saturn::A1[2]);
1238    let a2 = calculate_var(t, &saturn::A2[0], &saturn::A2[1], &saturn::A2[2]);
1239    let a3 = calculate_var(t, &saturn::A3[0], &saturn::A3[1], &saturn::A3[2]);
1240    let a4 = calculate_var(t, &saturn::A4[0], &saturn::A4[1], &saturn::A4[2]);
1241    let a5 = calculate_var(t, &saturn::A5[0], &saturn::A5[1], &saturn::A5[2]);
1242
1243    let l0 = calculate_var(t, &saturn::L0[0], &saturn::L0[1], &saturn::L0[2]);
1244    let l1 = calculate_var(t, &saturn::L1[0], &saturn::L1[1], &saturn::L1[2]);
1245    let l2 = calculate_var(t, &saturn::L2[0], &saturn::L2[1], &saturn::L2[2]);
1246    let l3 = calculate_var(t, &saturn::L3[0], &saturn::L3[1], &saturn::L3[2]);
1247    let l4 = calculate_var(t, &saturn::L4[0], &saturn::L4[1], &saturn::L4[2]);
1248    let l5 = calculate_var(t, &saturn::L5[0], &saturn::L5[1], &saturn::L5[2]);
1249
1250    let k0 = calculate_var(t, &saturn::K0[0], &saturn::K0[1], &saturn::K0[2]);
1251    let k1 = calculate_var(t, &saturn::K1[0], &saturn::K1[1], &saturn::K1[2]);
1252    let k2 = calculate_var(t, &saturn::K2[0], &saturn::K2[1], &saturn::K2[2]);
1253    let k3 = calculate_var(t, &saturn::K3[0], &saturn::K3[1], &saturn::K3[2]);
1254    let k4 = calculate_var(t, &saturn::K4[0], &saturn::K4[1], &saturn::K4[2]);
1255    let k5 = calculate_var(t, &saturn::K5[0], &saturn::K5[1], &saturn::K5[2]);
1256
1257    let h0 = calculate_var(t, &saturn::H0[0], &saturn::H0[1], &saturn::H0[2]);
1258    let h1 = calculate_var(t, &saturn::H1[0], &saturn::H1[1], &saturn::H1[2]);
1259    let h2 = calculate_var(t, &saturn::H2[0], &saturn::H2[1], &saturn::H2[2]);
1260    let h3 = calculate_var(t, &saturn::H3[0], &saturn::H3[1], &saturn::H3[2]);
1261    let h4 = calculate_var(t, &saturn::H4[0], &saturn::H4[1], &saturn::H4[2]);
1262    let h5 = calculate_var(t, &saturn::H5[0], &saturn::H5[1], &saturn::H5[2]);
1263
1264    let q0 = calculate_var(t, &saturn::Q0[0], &saturn::Q0[1], &saturn::Q0[2]);
1265    let q1 = calculate_var(t, &saturn::Q1[0], &saturn::Q1[1], &saturn::Q1[2]);
1266    let q2 = calculate_var(t, &saturn::Q2[0], &saturn::Q2[1], &saturn::Q2[2]);
1267    let q3 = calculate_var(t, &saturn::Q3[0], &saturn::Q3[1], &saturn::Q3[2]);
1268    let q4 = calculate_var(t, &saturn::Q4[0], &saturn::Q4[1], &saturn::Q4[2]);
1269
1270    let p0 = calculate_var(t, &saturn::P0[0], &saturn::P0[1], &saturn::P0[2]);
1271    let p1 = calculate_var(t, &saturn::P1[0], &saturn::P1[1], &saturn::P1[2]);
1272    let p2 = calculate_var(t, &saturn::P2[0], &saturn::P2[1], &saturn::P2[2]);
1273    let p3 = calculate_var(t, &saturn::P3[0], &saturn::P3[1], &saturn::P3[2]);
1274
1275    // We calculate the `t` potencies beforehand for easy re-use.
1276    let t2 = t * t;
1277    let t3 = t2 * t;
1278    let t4 = t2 * t2;
1279    let t5 = t2 * t3;
1280
1281    let a = a0 + a1 * t + a2 * t2 + a3 * t3 + a4 * t4 + a5 * t5;
1282    let l = (l0 + l1 * t + l2 * t2 + l3 * t3 + l4 * t4 + l5 * t5) % (2_f64 * PI);
1283    let k = k0 + k1 * t + k2 * t2 + k3 * t3 + k4 * t4 + k5 * t5;
1284    let h = h0 + h1 * t + h2 * t2 + h3 * t3 + h4 * t4 + h5 * t5;
1285    let q = q0 + q1 * t + q2 * t2 + q3 * t3 + q4 * t4;
1286    let p = p0 + p1 * t + p2 * t2 + p3 * t3;
1287
1288    VSOP87Elements {
1289        a,
1290        l: if l > 0_f64 { l } else { 2_f64 * PI + l },
1291        k,
1292        h,
1293        q,
1294        p,
1295    }
1296}
1297
1298/// Calculates VSOP87 solution for Uranus.
1299///
1300/// This function calculates the VSOP87 solution (heliocentric ecliptic orbital elements for the
1301/// equinox J2000.0) for the planet Uranus. The parameter needed is the Julian Day (*JD*) for the
1302/// given date. It returns the `VSOP87Elements` of the VSOP87 solution.
1303///
1304/// # Example
1305///
1306/// Given a date in [*JD*](http://aa.usno.navy.mil/data/docs/JulianDate.php), we can get the
1307/// orbit of the planet Uranus. In this case, we calculate the orbit of Uranus in December 19th,
1308/// 1399.
1309///
1310/// ```
1311/// let vsop87_elts = vsop87::uranus(2232395.0);
1312///
1313/// assert!(vsop87_elts.a > 19.2497356422 && vsop87_elts.a < 19.2497356424);
1314/// assert!(vsop87_elts.l > 4.5777275752 && vsop87_elts.l < 4.5777275754);
1315/// assert!(vsop87_elts.k > -0.0466529112 && vsop87_elts.k < -0.0466529110);
1316/// assert!(vsop87_elts.h > 0.0051308956 && vsop87_elts.h < 0.0051308958);
1317/// assert!(vsop87_elts.q > 0.0019206656 && vsop87_elts.q < 0.0019206658);
1318/// assert!(vsop87_elts.p > 0.00655819 && vsop87_elts.p < 0.00655895);
1319/// ```
1320///
1321/// It can then be converted into keplerian elements:
1322///
1323/// ```
1324/// use vsop87::{KeplerianElements, VSOP87Elements};
1325///
1326/// # let vsop87_elts = vsop87::uranus(2232395.0);
1327/// #
1328/// let k_elements: KeplerianElements = vsop87_elts.into();
1329/// let convert_back = VSOP87Elements::from(k_elements);
1330/// ```
1331#[must_use]
1332pub fn uranus(jde: f64) -> VSOP87Elements {
1333    let t = calculate_t(jde);
1334
1335    let a0 = calculate_var(t, &uranus::A0[0], &uranus::A0[1], &uranus::A0[2]);
1336    let a1 = calculate_var(t, &uranus::A1[0], &uranus::A1[1], &uranus::A1[2]);
1337    let a2 = calculate_var(t, &uranus::A2[0], &uranus::A2[1], &uranus::A2[2]);
1338    let a3 = calculate_var(t, &uranus::A3[0], &uranus::A3[1], &uranus::A3[2]);
1339    let a4 = calculate_var(t, &uranus::A4[0], &uranus::A4[1], &uranus::A4[2]);
1340    let a5 = calculate_var(t, &uranus::A5[0], &uranus::A5[1], &uranus::A5[2]);
1341
1342    let l0 = calculate_var(t, &uranus::L0[0], &uranus::L0[1], &uranus::L0[2]);
1343    let l1 = calculate_var(t, &uranus::L1[0], &uranus::L1[1], &uranus::L1[2]);
1344    let l2 = calculate_var(t, &uranus::L2[0], &uranus::L2[1], &uranus::L2[2]);
1345    let l3 = calculate_var(t, &uranus::L3[0], &uranus::L3[1], &uranus::L3[2]);
1346    let l4 = calculate_var(t, &uranus::L4[0], &uranus::L4[1], &uranus::L4[2]);
1347    let l5 = calculate_var(t, &uranus::L5[0], &uranus::L5[1], &uranus::L5[2]);
1348
1349    let k0 = calculate_var(t, &uranus::K0[0], &uranus::K0[1], &uranus::K0[2]);
1350    let k1 = calculate_var(t, &uranus::K1[0], &uranus::K1[1], &uranus::K1[2]);
1351    let k2 = calculate_var(t, &uranus::K2[0], &uranus::K2[1], &uranus::K2[2]);
1352    let k3 = calculate_var(t, &uranus::K3[0], &uranus::K3[1], &uranus::K3[2]);
1353    let k4 = calculate_var(t, &uranus::K4[0], &uranus::K4[1], &uranus::K4[2]);
1354
1355    let h0 = calculate_var(t, &uranus::H0[0], &uranus::H0[1], &uranus::H0[2]);
1356    let h1 = calculate_var(t, &uranus::H1[0], &uranus::H1[1], &uranus::H1[2]);
1357    let h2 = calculate_var(t, &uranus::H2[0], &uranus::H2[1], &uranus::H2[2]);
1358    let h3 = calculate_var(t, &uranus::H3[0], &uranus::H3[1], &uranus::H3[2]);
1359    let h4 = calculate_var(t, &uranus::H4[0], &uranus::H4[1], &uranus::H4[2]);
1360
1361    let q0 = calculate_var(t, &uranus::Q0[0], &uranus::Q0[1], &uranus::Q0[2]);
1362    let q1 = calculate_var(t, &uranus::Q1[0], &uranus::Q1[1], &uranus::Q1[2]);
1363    let q2 = calculate_var(t, &uranus::Q2[0], &uranus::Q2[1], &uranus::Q2[2]);
1364    let q3 = calculate_var(t, &uranus::Q3[0], &uranus::Q3[1], &uranus::Q3[2]);
1365
1366    let p0 = calculate_var(t, &uranus::P0[0], &uranus::P0[1], &uranus::P0[2]);
1367    let p1 = calculate_var(t, &uranus::P1[0], &uranus::P1[1], &uranus::P1[2]);
1368    let p2 = calculate_var(t, &uranus::P2[0], &uranus::P2[1], &uranus::P2[2]);
1369
1370    // We calculate the `t` potencies beforehand for easy re-use.
1371    let t2 = t * t;
1372    let t3 = t2 * t;
1373    let t4 = t2 * t2;
1374    let t5 = t2 * t3;
1375
1376    let a = a0 + a1 * t + a2 * t2 + a3 * t3 + a4 * t4 + a5 * t5;
1377    let l = (l0 + l1 * t + l2 * t2 + l3 * t3 + l4 * t4 + l5 * t5) % (2_f64 * PI);
1378    let k = k0 + k1 * t + k2 * t2 + k3 * t3 + k4 * t4;
1379    let h = h0 + h1 * t + h2 * t2 + h3 * t3 + h4 * t4;
1380    let q = q0 + q1 * t + q2 * t2 + q3 * t3;
1381    let p = p0 + p1 * t + p2 * t2;
1382
1383    VSOP87Elements {
1384        a,
1385        l: if l > 0_f64 { l } else { 2_f64 * PI + l },
1386        k,
1387        h,
1388        q,
1389        p,
1390    }
1391}
1392
1393/// Calculates VSOP87 solution for Neptune.
1394///
1395/// This function calculates the VSOP87 solution (heliocentric ecliptic orbital elements for the
1396/// equinox J2000.0) for the planet Neptune. The parameter needed is the Julian Day (*JD*) for the
1397/// given date. It returns the `VSOP87Elements` of the VSOP87 solution.
1398///
1399/// # Example
1400///
1401/// Given a date in [*JD*](http://aa.usno.navy.mil/data/docs/JulianDate.php), we can get the
1402/// orbit of the planet Neptune. In this case, we calculate the orbit of Neptune in December 19th,
1403/// 1499.
1404///
1405/// ```
1406/// let vsop87_elts = vsop87::neptune(2268920.0);
1407///
1408/// assert!(vsop87_elts.a > 30.1963044187 && vsop87_elts.a < 30.1963044189);
1409/// assert!(vsop87_elts.l > 5.1088676118 && vsop87_elts.l < 5.1088676120);
1410/// assert!(vsop87_elts.k > 0.0091964091 && vsop87_elts.k < 0.0091964093);
1411/// assert!(vsop87_elts.h > 0.0031103619 && vsop87_elts.h < 0.0031103621);
1412/// assert!(vsop87_elts.q > -0.0102800265 && vsop87_elts.q < -0.0102800263);
1413/// assert!(vsop87_elts.p > 0.01148076 && vsop87_elts.p < 0.01148152);
1414/// ```
1415///
1416/// It can then be converted into keplerian elements:
1417///
1418/// ```
1419/// use vsop87::{KeplerianElements, VSOP87Elements};
1420///
1421/// # let vsop87_elts = vsop87::neptune(2268920.0);
1422/// #
1423/// let k_elements: KeplerianElements = vsop87_elts.into();
1424/// let convert_back = VSOP87Elements::from(k_elements);
1425/// ```
1426#[must_use]
1427pub fn neptune(jde: f64) -> VSOP87Elements {
1428    let t = calculate_t(jde);
1429
1430    let a0 = calculate_var(t, &neptune::A0[0], &neptune::A0[1], &neptune::A0[2]);
1431    let a1 = calculate_var(t, &neptune::A1[0], &neptune::A1[1], &neptune::A1[2]);
1432    let a2 = calculate_var(t, &neptune::A2[0], &neptune::A2[1], &neptune::A2[2]);
1433    let a3 = calculate_var(t, &neptune::A3[0], &neptune::A3[1], &neptune::A3[2]);
1434    let a4 = calculate_var(t, &neptune::A4[0], &neptune::A4[1], &neptune::A4[2]);
1435    let a5 = calculate_var(t, &neptune::A5[0], &neptune::A5[1], &neptune::A5[2]);
1436
1437    let l0 = calculate_var(t, &neptune::L0[0], &neptune::L0[1], &neptune::L0[2]);
1438    let l1 = calculate_var(t, &neptune::L1[0], &neptune::L1[1], &neptune::L1[2]);
1439    let l2 = calculate_var(t, &neptune::L2[0], &neptune::L2[1], &neptune::L2[2]);
1440    let l3 = calculate_var(t, &neptune::L3[0], &neptune::L3[1], &neptune::L3[2]);
1441    let l4 = calculate_var(t, &neptune::L4[0], &neptune::L4[1], &neptune::L4[2]);
1442    let l5 = calculate_var(t, &neptune::L5[0], &neptune::L5[1], &neptune::L5[2]);
1443
1444    let k0 = calculate_var(t, &neptune::K0[0], &neptune::K0[1], &neptune::K0[2]);
1445    let k1 = calculate_var(t, &neptune::K1[0], &neptune::K1[1], &neptune::K1[2]);
1446    let k2 = calculate_var(t, &neptune::K2[0], &neptune::K2[1], &neptune::K2[2]);
1447    let k3 = calculate_var(t, &neptune::K3[0], &neptune::K3[1], &neptune::K3[2]);
1448    let k4 = calculate_var(t, &neptune::K4[0], &neptune::K4[1], &neptune::K4[2]);
1449    let k5 = calculate_var(t, &neptune::K5[0], &neptune::K5[1], &neptune::K5[2]);
1450
1451    let h0 = calculate_var(t, &neptune::H0[0], &neptune::H0[1], &neptune::H0[2]);
1452    let h1 = calculate_var(t, &neptune::H1[0], &neptune::H1[1], &neptune::H1[2]);
1453    let h2 = calculate_var(t, &neptune::H2[0], &neptune::H2[1], &neptune::H2[2]);
1454    let h3 = calculate_var(t, &neptune::H3[0], &neptune::H3[1], &neptune::H3[2]);
1455    let h4 = calculate_var(t, &neptune::H4[0], &neptune::H4[1], &neptune::H4[2]);
1456    let h5 = calculate_var(t, &neptune::H5[0], &neptune::H5[1], &neptune::H5[2]);
1457
1458    let q0 = calculate_var(t, &neptune::Q0[0], &neptune::Q0[1], &neptune::Q0[2]);
1459    let q1 = calculate_var(t, &neptune::Q1[0], &neptune::Q1[1], &neptune::Q1[2]);
1460    let q2 = calculate_var(t, &neptune::Q2[0], &neptune::Q2[1], &neptune::Q2[2]);
1461    let q3 = calculate_var(t, &neptune::Q3[0], &neptune::Q3[1], &neptune::Q3[2]);
1462
1463    let p0 = calculate_var(t, &neptune::P0[0], &neptune::P0[1], &neptune::P0[2]);
1464    let p1 = calculate_var(t, &neptune::P1[0], &neptune::P1[1], &neptune::P1[2]);
1465    let p2 = calculate_var(t, &neptune::P2[0], &neptune::P2[1], &neptune::P2[2]);
1466
1467    // We calculate the `t` potencies beforehand for easy re-use.
1468    let t2 = t * t;
1469    let t3 = t2 * t;
1470    let t4 = t2 * t2;
1471    let t5 = t2 * t3;
1472
1473    let a = a0 + a1 * t + a2 * t2 + a3 * t3 + a4 * t4 + a5 * t5;
1474    let l = (l0 + l1 * t + l2 * t2 + l3 * t3 + l4 * t4 + l5 * t5) % (2_f64 * PI);
1475    let k = k0 + k1 * t + k2 * t2 + k3 * t3 + k4 * t4 + k5 * t5;
1476    let h = h0 + h1 * t + h2 * t2 + h3 * t3 + h4 * t4 + h5 * t5;
1477    let q = q0 + q1 * t + q2 * t2 + q3 * t3;
1478    let p = p0 + p1 * t + p2 * t2;
1479
1480    VSOP87Elements {
1481        a,
1482        l: if l > 0_f64 { l } else { 2_f64 * PI + l },
1483        k,
1484        h,
1485        q,
1486        p,
1487    }
1488}