Skip to content

Commit 2abee1b

Browse files
committed
update to use precalculated data for available years
1 parent fe992c9 commit 2abee1b

File tree

4 files changed

+487
-195
lines changed

4 files changed

+487
-195
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
target/*
2+
build/*

src/bikram.rs

Lines changed: 216 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -1,165 +1,216 @@
1-
use std::f64::consts::PI;
2-
use chrono::NaiveDate;
3-
pub struct Bikram {
4-
year: i32,
5-
month: i32,
6-
day: i32,
7-
yuga_rotation_star: f64,
8-
yuga_rotation_sun: f64,
9-
yuga_civil_days: f64,
10-
planet_apogee_sun: f64,
11-
planet_circum_sun: f64,
12-
rad: f64,
13-
}
14-
15-
impl Bikram {
16-
pub fn new() -> Self {
17-
Self {
18-
year: 0,
19-
month: -1,
20-
day: 0,
21-
yuga_rotation_star: 1582237828.0,
22-
yuga_rotation_sun: 4320000.0,
23-
yuga_civil_days: 0.0,
24-
planet_apogee_sun: 77.0 + 17.0 / 60.0,
25-
planet_circum_sun: 13.0 + 50.0 / 60.0,
26-
rad: 180.0 / PI,
27-
}
28-
}
29-
30-
pub fn get_saura_masa_day(&self, ahar: i64) -> (i32, i32) {
31-
if self.today_saura_masa_first_p(ahar) {
32-
let tslong_tomorrow = self.get_tslong(ahar + 1);
33-
let month = ((tslong_tomorrow / 30.0) as i32) % 12;
34-
(month, 1)
35-
} else {
36-
let (prev_month, mut day) = self.get_saura_masa_day(ahar - 1);
37-
day += 1;
38-
(prev_month, day)
39-
}
40-
}
41-
42-
pub fn today_saura_masa_first_p(&self, ahar: i64) -> bool {
43-
let tslong_today = self.get_tslong(ahar) % 30.0;
44-
let tslong_tomorrow = self.get_tslong(ahar + 1) % 30.0;
45-
tslong_today > 25.0 && tslong_tomorrow < 5.0
46-
}
47-
48-
pub fn get_tslong(&self, ahar: i64) -> f64 {
49-
let t1 = (self.yuga_rotation_sun * ahar as f64 / self.yuga_civil_days) % 1.0;
50-
let mslong = 360.0 * t1;
51-
let x1 = mslong - self.planet_apogee_sun;
52-
let y1 = self.planet_circum_sun / 360.0;
53-
let y2 = (x1 / self.rad).sin();
54-
let y3 = y1 * y2;
55-
let x2 = (y3.asin()) * self.rad;
56-
mslong - x2
57-
}
58-
59-
pub fn get_julian_date(&self, year: i32, month: i32, day: i32) -> f64 {
60-
let (mut year, mut month) = (year, month);
61-
if month <= 2 {
62-
year -= 1;
63-
month += 12;
64-
}
65-
let a = (year as f64 / 100.0).floor();
66-
let b = 2.0 - a + (a / 4.0).floor();
67-
(365.25 * (year as f64 + 4716.0)).floor() + (30.6001 * (month as f64 + 1.0)).floor() + day as f64 + b - 1524.5
68-
}
69-
70-
pub fn from_julian_date(&self, julian_date: f64) -> (i32, i32, i32) {
71-
let z = (julian_date + 0.5).floor() as i32;
72-
let a = if z < 2299161 {
73-
z
74-
} else {
75-
let alpha = ((z as f64 - 1867216.25) / 36524.25).floor() as i32;
76-
z + 1 + alpha - (alpha / 4)
77-
};
78-
let b = a + 1524;
79-
let c = ((b as f64 - 122.1) / 365.25).floor() as i32;
80-
let d = (365.25 * c as f64).floor() as i32;
81-
let e = ((b - d) as f64 / 30.6001).floor() as i32;
82-
83-
let day = b - d - (30.6001 * e as f64) as i32;
84-
let month = if e < 14 { e - 1 } else { e - 13 };
85-
let year = if month > 2 { c - 4716 } else { c - 4715 };
86-
87-
(year, month, day)
88-
}
89-
90-
pub fn from_gregorian(&mut self, y: i32, m: i32, d: i32) {
91-
self.yuga_civil_days = self.yuga_rotation_star - self.yuga_rotation_sun;
92-
let julian = self.get_julian_date(y, m, d);
93-
let ahar = julian as i64 - 588465;
94-
let (saura_masa_num, saura_masa_day) = self.get_saura_masa_day(ahar);
95-
let year_kali = (ahar as f64 * self.yuga_rotation_sun / self.yuga_civil_days).floor() as i64;
96-
let year_saka = year_kali - 3179;
97-
let nepalimonth = saura_masa_num % 12;
98-
self.year = (year_saka + 135 + ((saura_masa_num - nepalimonth) / 12) as i64) as i32;
99-
self.month = (saura_masa_num + 12) % 12;
100-
self.day = saura_masa_day;
101-
}
102-
103-
pub fn to_gregorian(&mut self, bs_year: i32, bs_month: i32, bs_day: i32) -> (i32, i32, i32) {
104-
let mut bs_month = bs_month;
105-
self.yuga_civil_days = self.yuga_rotation_star - self.yuga_rotation_sun;
106-
let year_saka = bs_year - 135;
107-
let year_kali = year_saka + 3179;
108-
let mut ahar = ((year_kali as f64 * self.yuga_civil_days) / self.yuga_rotation_sun).floor() as i64;
109-
let mut saura_masa_num;
110-
let mut saura_masa_day;
111-
bs_month = (bs_month + 11) % 12;
112-
loop {
113-
let result = self.get_saura_masa_day(ahar);
114-
saura_masa_num = result.0;
115-
saura_masa_day = result.1;
116-
if saura_masa_num == bs_month && saura_masa_day == bs_day {
117-
break;
118-
}
119-
ahar += if saura_masa_num < bs_month || (saura_masa_num == bs_month && saura_masa_day < bs_day) {
120-
1
121-
} else {
122-
-1
123-
};
124-
}
125-
let julian_date = ahar as f64 + 588465.5;
126-
self.from_julian_date(julian_date)
127-
}
128-
129-
pub fn from_nepali(&mut self, bs_year: i32, bs_month: i32, bs_day: i32) {
130-
let (g_year, g_month, g_day) = self.to_gregorian(bs_year, bs_month, bs_day);
131-
self.year = g_year;
132-
self.month = g_month - 1;
133-
self.day = g_day;
134-
}
135-
136-
pub fn get_year(&self) -> i32 {
137-
self.year
138-
}
139-
140-
pub fn get_month(&self) -> i32 {
141-
self.month + 1
142-
}
143-
144-
pub fn get_day(&self) -> i32 {
145-
self.day
146-
}
147-
148-
pub fn get_weekday_name(&self, year: i32, month: i32, day: i32) -> String {
149-
if let Some(date) = NaiveDate::from_ymd_opt(year, month as u32, day as u32) {
150-
date.format("%A").to_string()
151-
} else {
152-
"Invalid date".to_string() // Handle invalid date case
153-
}
154-
}
155-
156-
pub fn days_in_month(&mut self, bs_year: i32, bs_month: i32) -> i32 {
157-
let next_month = (bs_month % 12) + 1;
158-
let next_year = if bs_month == 12 { bs_year + 1 } else { bs_year };
159-
let (g_year_start, g_month_start, g_day_start) = self.to_gregorian(bs_year, bs_month, 1);
160-
let julian_start = self.get_julian_date(g_year_start, g_month_start, g_day_start);
161-
let (g_year_end, g_month_end, g_day_end) = self.to_gregorian(next_year, next_month, 1);
162-
let julian_end = self.get_julian_date(g_year_end, g_month_end, g_day_end);
163-
(julian_end - julian_start) as i32
164-
}
165-
}
1+
/*
2+
* Copyright (C) 2024 Khumnath CG
3+
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*/
18+
19+
20+
21+
#[allow(dead_code)] // Allow unused fields that are initialized and used for derived values
22+
pub struct Bikram {
23+
year: i32,
24+
month: i32,
25+
day: i32,
26+
yuga_rotation_star: f64,
27+
yuga_rotation_sun: f64,
28+
yuga_civil_days: f64,
29+
planet_apogee_sun: f64,
30+
planet_circum_sun: f64,
31+
}
32+
33+
const RAD: f64 = 180.0 / std::f64::consts::PI;
34+
const BS_START_YEAR: i32 = 2000;
35+
const JULIAN_EPOCH_2000_BS: f64 = 2430910.5; // 14 April 1943
36+
37+
include!("./precomputed_data.rs"); // Contains NP_MONTHS_DATA: [[i32; 13]; N], NP_DATA_YEAR_COUNT
38+
39+
impl Bikram {
40+
pub fn new() -> Self {
41+
let yuga_rotation_star = 1582237828.0;
42+
let yuga_rotation_sun = 4320000.0;
43+
Self {
44+
year: 0,
45+
month: -1,
46+
day: 0,
47+
yuga_rotation_star,
48+
yuga_rotation_sun,
49+
yuga_civil_days: yuga_rotation_star - yuga_rotation_sun,
50+
planet_apogee_sun: 77.0 + 17.0 / 60.0,
51+
planet_circum_sun: 13.0 + 50.0 / 60.0,
52+
}
53+
}
54+
55+
pub fn from_gregorian(&mut self, y: i32, m: i32, d: i32) {
56+
let julian = self.get_julian_date(y, m, d);
57+
let diff = (julian - JULIAN_EPOCH_2000_BS) as i64;
58+
let mut bs_year = BS_START_YEAR;
59+
if diff >= 0 {
60+
// Ensure we don't go out of bounds for NP_MONTHS_DATA
61+
while (bs_year - BS_START_YEAR) < NP_DATA_YEAR_COUNT as i32 {
62+
let mut current_year_diff = diff;
63+
let mut found_in_precomputed = false;
64+
for month_idx in 0..12 {
65+
let days = NP_MONTHS_DATA[(bs_year - BS_START_YEAR) as usize][month_idx];
66+
if current_year_diff < days as i64 {
67+
self.year = bs_year;
68+
self.month = (month_idx + 1) as i32;
69+
self.day = (current_year_diff + 1) as i32;
70+
found_in_precomputed = true;
71+
break;
72+
} else {
73+
current_year_diff -= days as i64;
74+
}
75+
}
76+
if found_in_precomputed {
77+
return;
78+
}
79+
bs_year += 1; // Move to the next year
80+
}
81+
}
82+
// Fallback to astronomical if outside precomputed range or diff is negative
83+
self.from_gregorian_astronomical(y, m, d);
84+
}
85+
86+
pub fn to_gregorian(&self, bs_year: i32, bs_month: i32, bs_day: i32) -> (i32, i32, i32) {
87+
if bs_year >= BS_START_YEAR && (bs_year - BS_START_YEAR) < NP_DATA_YEAR_COUNT as i32 {
88+
let mut days = 0;
89+
for y_idx in 0..(bs_year - BS_START_YEAR) {
90+
days += NP_MONTHS_DATA[y_idx as usize][12]; // Total days in that BS year
91+
}
92+
for m_idx in 0..(bs_month - 1) {
93+
days += NP_MONTHS_DATA[(bs_year - BS_START_YEAR) as usize][m_idx as usize];
94+
}
95+
days += bs_day - 1;
96+
let target_julian = JULIAN_EPOCH_2000_BS + days as f64;
97+
return self.from_julian_date(target_julian);
98+
}
99+
self.to_gregorian_astronomical(bs_year, bs_month, bs_day)
100+
}
101+
102+
fn from_gregorian_astronomical(&mut self, y: i32, m: i32, d: i32) {
103+
let julian = self.get_julian_date(y, m, d);
104+
let ahar = (julian - 588465.5) as i64;
105+
let (sm_num, sm_day) = self.get_saura_masa_day(ahar);
106+
let year_kali = ((ahar as f64 * self.yuga_rotation_sun) / self.yuga_civil_days).floor() as i64;
107+
let year_saka = year_kali - 3179;
108+
let nepali_month = sm_num % 12;
109+
self.year = (year_saka + 135 + ((sm_num - nepali_month) / 12) as i64) as i32;
110+
self.month = ((sm_num + 12) % 12) + 1;
111+
self.day = sm_day;
112+
}
113+
114+
fn to_gregorian_astronomical(&self, bs_year: i32, bs_month: i32, bs_day: i32) -> (i32, i32, i32) {
115+
let year_saka = bs_year - 135;
116+
let year_kali = year_saka + 3179;
117+
let mut ahar = ((year_kali as f64 * self.yuga_civil_days) / self.yuga_rotation_sun).floor() as i64;
118+
// Removed 'mut' as it's not reassigned after initialization
119+
let bs_month_zero_indexed = (bs_month + 11) % 12; // Convert to 0-indexed for comparison
120+
loop {
121+
let (sm, sd) = self.get_saura_masa_day(ahar);
122+
if sm == bs_month_zero_indexed && sd == bs_day {
123+
break;
124+
}
125+
ahar += if sm < bs_month_zero_indexed || (sm == bs_month_zero_indexed && sd < bs_day) { 1 } else { -1 };
126+
}
127+
let jd = ahar as f64 + 588465.5;
128+
self.from_julian_date(jd)
129+
}
130+
131+
fn get_saura_masa_day(&self, ahar: i64) -> (i32, i32) {
132+
if self.today_saura_masa_first_p(ahar) {
133+
let tslong_tomorrow = self.get_tslong(ahar + 1);
134+
let month = ((tslong_tomorrow / 30.0) as i32 + 12) % 12;
135+
(month, 1)
136+
} else {
137+
let (prev_month, mut day) = self.get_saura_masa_day(ahar - 1);
138+
day += 1;
139+
(prev_month, day)
140+
}
141+
}
142+
143+
fn today_saura_masa_first_p(&self, ahar: i64) -> bool {
144+
let today = self.get_tslong(ahar) % 30.0;
145+
let tomorrow = self.get_tslong(ahar + 1) % 30.0;
146+
// Handle negative modulo results for consistency if any intermediate calculation leads to it
147+
let today_mod = (today + 30.0) % 30.0;
148+
let tomorrow_mod = (tomorrow + 30.0) % 30.0;
149+
today_mod > 25.0 && tomorrow_mod < 5.0
150+
}
151+
152+
fn get_tslong(&self, ahar: i64) -> f64 {
153+
let mut t1 = (self.yuga_rotation_sun * ahar as f64) / self.yuga_civil_days;
154+
t1 -= t1.floor();
155+
let mslong = 360.0 * t1;
156+
let x1 = mslong - self.planet_apogee_sun;
157+
let y1 = self.planet_circum_sun / 360.0;
158+
let y2 = (x1 / RAD).sin();
159+
let y3 = y1 * y2;
160+
let x2 = y3.asin() * RAD;
161+
mslong - x2
162+
}
163+
164+
fn get_julian_date(&self, y: i32, m: i32, d: i32) -> f64 {
165+
let (mut y, mut m) = (y, m);
166+
if m <= 2 {
167+
y -= 1;
168+
m += 12;
169+
}
170+
let a = (y as f64 / 100.0).floor();
171+
let b = 2.0 - a + (a / 4.0).floor();
172+
(365.25 * (y as f64 + 4716.0)).floor()
173+
+ (30.6001 * (m as f64 + 1.0)).floor()
174+
+ d as f64 + b - 1524.5
175+
}
176+
177+
fn from_julian_date(&self, jd: f64) -> (i32, i32, i32) {
178+
let z = (jd + 0.5).floor() as i32;
179+
let a = if z < 2299161 {
180+
z
181+
} else {
182+
let alpha = ((z as f64 - 1867216.25) / 36524.25).floor() as i32;
183+
z + 1 + alpha - (alpha / 4)
184+
};
185+
let b = a + 1524;
186+
let c = ((b as f64 - 122.1) / 365.25).floor() as i32;
187+
let d = (365.25 * c as f64).floor() as i32;
188+
let e = ((b - d) as f64 / 30.6001).floor() as i32;
189+
let day = b - d - (30.6001 * e as f64) as i32;
190+
let month = if e < 14 { e - 1 } else { e - 13 };
191+
let year = if month > 2 { c - 4716 } else { c - 4715 };
192+
(year, month, day)
193+
}
194+
195+
pub fn get_year(&self) -> i32 { self.year }
196+
pub fn get_month(&self) -> i32 { self.month }
197+
pub fn get_day(&self) -> i32 { self.day }
198+
199+
pub fn days_in_month(&self, bs_year: i32, bs_month: i32) -> i32 {
200+
if bs_year >= BS_START_YEAR && (bs_year - BS_START_YEAR) < NP_DATA_YEAR_COUNT as i32 {
201+
NP_MONTHS_DATA[(bs_year - BS_START_YEAR) as usize][(bs_month - 1) as usize]
202+
} else {
203+
let (gy1, gm1, gd1) = self.to_gregorian(bs_year, bs_month, 1);
204+
let (gy2, gm2, gd2) = if bs_month == 12 {
205+
self.to_gregorian(bs_year + 1, 1, 1)
206+
} else {
207+
self.to_gregorian(bs_year, bs_month + 1, 1)
208+
};
209+
let jd1 = self.get_julian_date(gy1, gm1, gd1);
210+
let jd2 = self.get_julian_date(gy2, gm2, gd2);
211+
(jd2 - jd1) as i32
212+
}
213+
}
214+
215+
}
216+

0 commit comments

Comments
 (0)