diff --git a/Cargo.lock b/Cargo.lock index 3cfb9ccd5..115669b5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2433,8 +2433,6 @@ dependencies = [ [[package]] name = "rawdb" version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f53bd783ecee89e4b43e82634a355a28c74743e89596772287dbe06bb95942" dependencies = [ "libc", "log", @@ -3253,8 +3251,6 @@ checksum = "8f54a172d0620933a27a4360d3db3e2ae0dd6cceae9730751a036bbf182c4b23" [[package]] name = "vecdb" version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "867dfa2136db3ff905f3668eb0ed76daf8de14237338c1d498dbd7ce925bf90e" dependencies = [ "ctrlc", "log", @@ -3274,8 +3270,6 @@ dependencies = [ [[package]] name = "vecdb_derive" version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1819035219d794f6dd96feab3a700aa582e193ddd4ac730ef771c0d31ea03260" dependencies = [ "quote", "syn", @@ -3694,18 +3688,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71ddd76bcebeed25db614f82bf31a9f4222d3fbba300e6fb6c00afa26cbd4d9d" +checksum = "fdea86ddd5568519879b8187e1cf04e24fce28f7fe046ceecbce472ff19a2572" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8187381b52e32220d50b255276aa16a084ec0a9017a0ca2152a1f55c539758d" +checksum = "0c15e1b46eff7c6c91195752e0eeed8ef040e391cdece7c25376957d5f15df22" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index eca9a9b42..3ed37a2bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,8 +82,8 @@ tokio = { version = "1.49.0", features = ["rt-multi-thread"] } tracing = { version = "0.1", default-features = false, features = ["std"] } tower-http = { version = "0.6.8", features = ["catch-panic", "compression-br", "compression-gzip", "compression-zstd", "cors", "normalize-path", "timeout", "trace"] } tower-layer = "0.3" -vecdb = { version = "0.6.2", features = ["derive", "serde_json", "pco", "schemars"] } -# vecdb = { path = "../anydb/crates/vecdb", features = ["derive", "serde_json", "pco", "schemars"] } +# vecdb = { version = "0.6.2", features = ["derive", "serde_json", "pco", "schemars"] } +vecdb = { path = "../anydb/crates/vecdb", features = ["derive", "serde_json", "pco", "schemars"] } [workspace.metadata.release] shared-version = true diff --git a/crates/brk_bindgen/src/generators/javascript/client.rs b/crates/brk_bindgen/src/generators/javascript/client.rs index e0b00918c..173c21b44 100644 --- a/crates/brk_bindgen/src/generators/javascript/client.rs +++ b/crates/brk_bindgen/src/generators/javascript/client.rs @@ -48,15 +48,120 @@ class BrkError extends Error {{ }} }} +// Date conversion constants and helpers +const _GENESIS = new Date(2009, 0, 3); // dateindex 0, weekindex 0 +const _DAY_ONE = new Date(2009, 0, 9); // dateindex 1 (6 day gap after genesis) +const _MS_PER_DAY = 24 * 60 * 60 * 1000; +const _MS_PER_WEEK = 7 * _MS_PER_DAY; +const _DATE_INDEXES = new Set(['dateindex', 'weekindex', 'monthindex', 'yearindex', 'quarterindex', 'semesterindex', 'decadeindex']); + +/** @param {{number}} months @returns {{globalThis.Date}} */ +const _addMonths = (months) => new Date(2009, months, 1); + +/** + * Convert an index value to a Date for date-based indexes. + * @param {{Index}} index - The index type + * @param {{number}} i - The index value + * @returns {{globalThis.Date}} + */ +function indexToDate(index, i) {{ + switch (index) {{ + case 'dateindex': return i === 0 ? _GENESIS : new Date(_DAY_ONE.getTime() + (i - 1) * _MS_PER_DAY); + case 'weekindex': return new Date(_GENESIS.getTime() + i * _MS_PER_WEEK); + case 'monthindex': return _addMonths(i); + case 'yearindex': return new Date(2009 + i, 0, 1); + case 'quarterindex': return _addMonths(i * 3); + case 'semesterindex': return _addMonths(i * 6); + case 'decadeindex': return new Date(2009 + i * 10, 0, 1); + default: throw new Error(`${{index}} is not a date-based index`); + }} +}} + +/** + * Check if an index type is date-based. + * @param {{Index}} index + * @returns {{boolean}} + */ +function isDateIndex(index) {{ + return _DATE_INDEXES.has(index); +}} + +/** + * Wrap raw metric data with helper methods. + * @template T + * @param {{MetricData}} raw - Raw JSON response + * @returns {{MetricData}} + */ +function _wrapMetricData(raw) {{ + const {{ index, start, end, data }} = raw; + return /** @type {{MetricData}} */ ({{ + ...raw, + dates() {{ + /** @type {{globalThis.Date[]}} */ + const result = []; + for (let i = start; i < end; i++) result.push(indexToDate(index, i)); + return result; + }}, + indexes() {{ + /** @type {{number[]}} */ + const result = []; + for (let i = start; i < end; i++) result.push(i); + return result; + }}, + toDateMap() {{ + /** @type {{Map}} */ + const map = new Map(); + for (let i = 0; i < data.length; i++) map.set(indexToDate(index, start + i), data[i]); + return map; + }}, + toIndexMap() {{ + /** @type {{Map}} */ + const map = new Map(); + for (let i = 0; i < data.length; i++) map.set(start + i, data[i]); + return map; + }}, + dateEntries() {{ + /** @type {{Array<[globalThis.Date, T]>}} */ + const result = []; + for (let i = 0; i < data.length; i++) result.push([indexToDate(index, start + i), data[i]]); + return result; + }}, + indexEntries() {{ + /** @type {{Array<[number, T]>}} */ + const result = []; + for (let i = 0; i < data.length; i++) result.push([start + i, data[i]]); + return result; + }}, + *iter() {{ + for (let i = 0; i < data.length; i++) yield [start + i, data[i]]; + }}, + *iterDates() {{ + for (let i = 0; i < data.length; i++) yield [indexToDate(index, start + i), data[i]]; + }}, + [Symbol.iterator]() {{ + return this.iter(); + }}, + }}); +}} + /** * @template T * @typedef {{Object}} MetricData * @property {{number}} version - Version of the metric data + * @property {{Index}} index - The index type used for this query * @property {{number}} total - Total number of data points * @property {{number}} start - Start index (inclusive) * @property {{number}} end - End index (exclusive) * @property {{string}} stamp - ISO 8601 timestamp of when the response was generated * @property {{T[]}} data - The metric data + * @property {{() => globalThis.Date[]}} dates - Convert index range to dates (date-based indexes only) + * @property {{() => number[]}} indexes - Get index range as array + * @property {{() => Map}} toDateMap - Return data as Map keyed by date (date-based only) + * @property {{() => Map}} toIndexMap - Return data as Map keyed by index + * @property {{() => Array<[globalThis.Date, T]>}} dateEntries - Return data as [date, value] pairs (date-based only) + * @property {{() => Array<[number, T]>}} indexEntries - Return data as [index, value] pairs + * @property {{() => IterableIterator<[number, T]>}} iter - Iterate over [index, value] pairs + * @property {{() => IterableIterator<[globalThis.Date, T]>}} iterDates - Iterate over [date, value] pairs (date-based only) */ /** @typedef {{MetricData}} AnyMetricData */ @@ -150,18 +255,18 @@ function _endpoint(client, name, index) {{ * @returns {{RangeBuilder}} */ const rangeBuilder = (start, end) => ({{ - fetch(onUpdate) {{ return client.getJson(buildPath(start, end), onUpdate); }}, + fetch(onUpdate) {{ return client._fetchMetricData(buildPath(start, end), onUpdate); }}, fetchCsv() {{ return client.getText(buildPath(start, end, 'csv')); }}, then(resolve, reject) {{ return this.fetch().then(resolve, reject); }}, }}); /** - * @param {{number}} index + * @param {{number}} idx * @returns {{SingleItemBuilder}} */ - const singleItemBuilder = (index) => ({{ - fetch(onUpdate) {{ return client.getJson(buildPath(index, index + 1), onUpdate); }}, - fetchCsv() {{ return client.getText(buildPath(index, index + 1, 'csv')); }}, + const singleItemBuilder = (idx) => ({{ + fetch(onUpdate) {{ return client._fetchMetricData(buildPath(idx, idx + 1), onUpdate); }}, + fetchCsv() {{ return client.getText(buildPath(idx, idx + 1, 'csv')); }}, then(resolve, reject) {{ return this.fetch().then(resolve, reject); }}, }}); @@ -171,19 +276,19 @@ function _endpoint(client, name, index) {{ */ const skippedBuilder = (start) => ({{ take(n) {{ return rangeBuilder(start, start + n); }}, - fetch(onUpdate) {{ return client.getJson(buildPath(start, undefined), onUpdate); }}, + fetch(onUpdate) {{ return client._fetchMetricData(buildPath(start, undefined), onUpdate); }}, fetchCsv() {{ return client.getText(buildPath(start, undefined, 'csv')); }}, then(resolve, reject) {{ return this.fetch().then(resolve, reject); }}, }}); /** @type {{MetricEndpointBuilder}} */ const endpoint = {{ - get(index) {{ return singleItemBuilder(index); }}, + get(idx) {{ return singleItemBuilder(idx); }}, slice(start, end) {{ return rangeBuilder(start, end); }}, first(n) {{ return rangeBuilder(undefined, n); }}, last(n) {{ return n === 0 ? rangeBuilder(undefined, 0) : rangeBuilder(-n, undefined); }}, skip(n) {{ return skippedBuilder(n); }}, - fetch(onUpdate) {{ return client.getJson(buildPath(), onUpdate); }}, + fetch(onUpdate) {{ return client._fetchMetricData(buildPath(), onUpdate); }}, fetchCsv() {{ return client.getText(buildPath(undefined, undefined, 'csv')); }}, then(resolve, reject) {{ return this.fetch().then(resolve, reject); }}, get path() {{ return p; }}, @@ -263,6 +368,19 @@ class BrkClientBase {{ const res = await this.get(path); return res.text(); }} + + /** + * Fetch metric data and wrap with helper methods (internal) + * @template T + * @param {{string}} path + * @param {{(value: MetricData) => void}} [onUpdate] + * @returns {{Promise>}} + */ + async _fetchMetricData(path, onUpdate) {{ + const wrappedOnUpdate = onUpdate ? (/** @type {{MetricData}} */ raw) => onUpdate(_wrapMetricData(raw)) : undefined; + const raw = await this.getJson(path, wrappedOnUpdate); + return _wrapMetricData(raw); + }} }} /** @@ -299,6 +417,31 @@ pub fn generate_static_constants(output: &mut String) { for (name, value) in CohortConstants::all() { write_static_const(output, name, &format_json(&camel_case_keys(value))); } + + // Helper methods + writeln!( + output, + r#" /** + * Convert an index value to a Date for date-based indexes. + * @param {{Index}} index - The index type + * @param {{number}} i - The index value + * @returns {{globalThis.Date}} + */ + indexToDate(index, i) {{ + return indexToDate(index, i); + }} + + /** + * Check if an index type is date-based. + * @param {{Index}} index + * @returns {{boolean}} + */ + isDateIndex(index) {{ + return isDateIndex(index); + }} +"# + ) + .unwrap(); } fn indent_json_const(json: &str) -> String { diff --git a/crates/brk_bindgen/src/generators/python/api.rs b/crates/brk_bindgen/src/generators/python/api.rs index dfaa5a468..94f9d2ec0 100644 --- a/crates/brk_bindgen/src/generators/python/api.rs +++ b/crates/brk_bindgen/src/generators/python/api.rs @@ -39,6 +39,16 @@ pub fn generate_main_client(output: &mut String, endpoints: &[Endpoint]) { writeln!(output, " return MetricEndpointBuilder(self, metric, index)").unwrap(); writeln!(output).unwrap(); + // Generate helper methods + writeln!(output, " def index_to_date(self, index: Index, i: int) -> date:").unwrap(); + writeln!(output, " \"\"\"Convert an index value to a date for date-based indexes.\"\"\"").unwrap(); + writeln!(output, " return index_to_date(index, i)").unwrap(); + writeln!(output).unwrap(); + writeln!(output, " def is_date_index(self, index: Index) -> bool:").unwrap(); + writeln!(output, " \"\"\"Check if an index type is date-based.\"\"\"").unwrap(); + writeln!(output, " return is_date_index(index)").unwrap(); + writeln!(output).unwrap(); + // Generate API methods generate_api_methods(output, endpoints); } diff --git a/crates/brk_bindgen/src/generators/python/client.rs b/crates/brk_bindgen/src/generators/python/client.rs index ef72cf225..fc05e8d48 100644 --- a/crates/brk_bindgen/src/generators/python/client.rs +++ b/crates/brk_bindgen/src/generators/python/client.rs @@ -131,16 +131,114 @@ def _p(prefix: str, acc: str) -> str: pub fn generate_endpoint_class(output: &mut String) { writeln!( output, - r#"@dataclass + r#"# Date conversion constants +_GENESIS = date(2009, 1, 3) # dateindex 0, weekindex 0 +_DAY_ONE = date(2009, 1, 9) # dateindex 1 (6 day gap after genesis) +_DATE_INDEXES = frozenset(['dateindex', 'weekindex', 'monthindex', 'yearindex', 'quarterindex', 'semesterindex', 'decadeindex']) + +def is_date_index(index: str) -> bool: + """Check if an index type is date-based.""" + return index in _DATE_INDEXES + +def index_to_date(index: str, i: int) -> date: + """Convert an index value to a date for date-based indexes.""" + if index == 'dateindex': + return _GENESIS if i == 0 else _DAY_ONE + timedelta(days=i - 1) + elif index == 'weekindex': + return _GENESIS + timedelta(weeks=i) + elif index == 'monthindex': + return date(2009 + i // 12, i % 12 + 1, 1) + elif index == 'yearindex': + return date(2009 + i, 1, 1) + elif index == 'quarterindex': + m = i * 3 + return date(2009 + m // 12, m % 12 + 1, 1) + elif index == 'semesterindex': + m = i * 6 + return date(2009 + m // 12, m % 12 + 1, 1) + elif index == 'decadeindex': + return date(2009 + i * 10, 1, 1) + else: + raise ValueError(f"{{index}} is not a date-based index") + + +@dataclass class MetricData(Generic[T]): """Metric data with range information.""" version: int + index: Index total: int start: int end: int stamp: str data: List[T] + def dates(self) -> List[date]: + """Convert index range to dates. Only works for date-based indexes.""" + return [index_to_date(self.index, i) for i in range(self.start, self.end)] + + def indexes(self) -> List[int]: + """Get index range as list.""" + return list(range(self.start, self.end)) + + def to_date_dict(self) -> dict[date, T]: + """Return data as {{date: value}} dict. Only works for date-based indexes.""" + return dict(zip(self.dates(), self.data)) + + def to_index_dict(self) -> dict[int, T]: + """Return data as {{index: value}} dict.""" + return dict(zip(range(self.start, self.end), self.data)) + + def date_items(self) -> List[Tuple[date, T]]: + """Return data as [(date, value), ...] pairs. Only works for date-based indexes.""" + return list(zip(self.dates(), self.data)) + + def index_items(self) -> List[Tuple[int, T]]: + """Return data as [(index, value), ...] pairs.""" + return list(zip(range(self.start, self.end), self.data)) + + def iter(self) -> Iterator[Tuple[int, T]]: + """Iterate over (index, value) pairs.""" + return iter(zip(range(self.start, self.end), self.data)) + + def iter_dates(self) -> Iterator[Tuple[date, T]]: + """Iterate over (date, value) pairs. Date-based indexes only.""" + return iter(zip(self.dates(), self.data)) + + def __iter__(self) -> Iterator[Tuple[int, T]]: + """Default iteration over (index, value) pairs.""" + return self.iter() + + def to_polars(self, with_dates: bool = True) -> pl.DataFrame: + """Convert to Polars DataFrame. Requires polars to be installed. + + Returns a DataFrame with columns: + - 'date' (date) and 'value' (T) if with_dates=True and index is date-based + - 'index' (int) and 'value' (T) otherwise + """ + try: + import polars as pl # type: ignore[import-not-found] + except ImportError: + raise ImportError("polars is required: pip install polars") + if with_dates and self.index in _DATE_INDEXES: + return pl.DataFrame({{"date": self.dates(), "value": self.data}}) + return pl.DataFrame({{"index": list(range(self.start, self.end)), "value": self.data}}) + + def to_pandas(self, with_dates: bool = True) -> pd.DataFrame: + """Convert to Pandas DataFrame. Requires pandas to be installed. + + Returns a DataFrame with columns: + - 'date' (date) and 'value' (T) if with_dates=True and index is date-based + - 'index' (int) and 'value' (T) otherwise + """ + try: + import pandas as pd # type: ignore[import-not-found] + except ImportError: + raise ImportError("pandas is required: pip install pandas") + if with_dates and self.index in _DATE_INDEXES: + return pd.DataFrame({{"date": self.dates(), "value": self.data}}) + return pd.DataFrame({{"index": list(range(self.start, self.end)), "value": self.data}}) + # Type alias for non-generic usage AnyMetricData = MetricData[Any] @@ -177,9 +275,8 @@ class _EndpointConfig: p = self.path() return f"{{p}}?{{query}}" if query else p - def get_json(self) -> MetricData: - data = self.client.get_json(self._build_path()) - return MetricData(**data) + def get_metric(self) -> MetricData: + return MetricData(**self.client.get_json(self._build_path())) def get_csv(self) -> str: return self.client.get_text(self._build_path(format='csv')) @@ -193,7 +290,7 @@ class RangeBuilder(Generic[T]): def fetch(self) -> MetricData[T]: """Fetch the range as parsed JSON.""" - return self._config.get_json() + return self._config.get_metric() def fetch_csv(self) -> str: """Fetch the range as CSV string.""" @@ -208,7 +305,7 @@ class SingleItemBuilder(Generic[T]): def fetch(self) -> MetricData[T]: """Fetch the single item.""" - return self._config.get_json() + return self._config.get_metric() def fetch_csv(self) -> str: """Fetch as CSV.""" @@ -231,7 +328,7 @@ class SkippedBuilder(Generic[T]): def fetch(self) -> MetricData[T]: """Fetch from skipped position to end.""" - return self._config.get_json() + return self._config.get_metric() def fetch_csv(self) -> str: """Fetch as CSV.""" @@ -315,7 +412,7 @@ class MetricEndpointBuilder(Generic[T]): def fetch(self) -> MetricData[T]: """Fetch all data as parsed JSON.""" - return self._config.get_json() + return self._config.get_metric() def fetch_csv(self) -> str: """Fetch all data as CSV string.""" diff --git a/crates/brk_bindgen/src/generators/python/mod.rs b/crates/brk_bindgen/src/generators/python/mod.rs index b1c5bcb1c..e1a6129c7 100644 --- a/crates/brk_bindgen/src/generators/python/mod.rs +++ b/crates/brk_bindgen/src/generators/python/mod.rs @@ -29,7 +29,7 @@ pub fn generate_python_client( writeln!(output, "from dataclasses import dataclass").unwrap(); writeln!( output, - "from typing import TypeVar, Generic, Any, Optional, List, Literal, TypedDict, Union, Protocol, overload" + "from typing import TypeVar, Generic, Any, Optional, List, Literal, TypedDict, Union, Protocol, overload, Iterator, Tuple, TYPE_CHECKING" ) .unwrap(); writeln!( @@ -38,7 +38,11 @@ pub fn generate_python_client( ) .unwrap(); writeln!(output, "from urllib.parse import urlparse").unwrap(); + writeln!(output, "from datetime import date, timedelta").unwrap(); writeln!(output, "import json\n").unwrap(); + writeln!(output, "if TYPE_CHECKING:").unwrap(); + writeln!(output, " import pandas as pd # type: ignore[import-not-found]").unwrap(); + writeln!(output, " import polars as pl # type: ignore[import-not-found]\n").unwrap(); writeln!(output, "T = TypeVar('T')\n").unwrap(); types::generate_type_definitions(&mut output, schemas); diff --git a/crates/brk_bindgen/tests/catalog_test.rs b/crates/brk_bindgen/tests/catalog_test.rs index fbe7b53fb..ff0ec13d2 100644 --- a/crates/brk_bindgen/tests/catalog_test.rs +++ b/crates/brk_bindgen/tests/catalog_test.rs @@ -518,13 +518,18 @@ fn test_generated_python_output() { let mut py_output = String::new(); writeln!(py_output, "# Auto-generated BRK Python client").unwrap(); writeln!(py_output, "# Do not edit manually\n").unwrap(); - writeln!(py_output, "from typing import TypeVar, Generic, Any, Optional, List, Literal, TypedDict, Union, Protocol, overload").unwrap(); + writeln!(py_output, "from typing import TypeVar, Generic, Any, Optional, List, Literal, TypedDict, Union, Protocol, overload, Iterator, Tuple, TYPE_CHECKING").unwrap(); + writeln!(py_output, "\nif TYPE_CHECKING:").unwrap(); + writeln!(py_output, " import pandas as pd # type: ignore[import-not-found]").unwrap(); + writeln!(py_output, " import polars as pl # type: ignore[import-not-found]").unwrap(); writeln!( py_output, "from http.client import HTTPSConnection, HTTPConnection" ) .unwrap(); writeln!(py_output, "from urllib.parse import urlparse").unwrap(); + writeln!(py_output, "from datetime import date, timedelta").unwrap(); + writeln!(py_output, "from dataclasses import dataclass").unwrap(); writeln!(py_output, "import json\n").unwrap(); writeln!(py_output, "T = TypeVar('T')\n").unwrap(); diff --git a/crates/brk_computer/examples/computer.rs b/crates/brk_computer/examples/computer.rs index a6cb99b37..c18c41a35 100644 --- a/crates/brk_computer/examples/computer.rs +++ b/crates/brk_computer/examples/computer.rs @@ -56,7 +56,7 @@ fn run() -> Result<()> { // Pre-run indexer if too far behind, then drop and reimport to reduce memory let chain_height = client.get_last_height()?; let indexed_height = indexer.vecs.starting_height(); - if u32::from(chain_height) - u32::from(indexed_height) > 1000 { + if u32::from(chain_height).saturating_sub(u32::from(indexed_height)) > 1000 { indexer.checked_index(&blocks, &client, &exit)?; drop(indexer); Mimalloc::collect(); diff --git a/crates/brk_computer/src/distribution/address/indexes/any.rs b/crates/brk_computer/src/distribution/address/indexes/any.rs index 2221c60a6..406a0a447 100644 --- a/crates/brk_computer/src/distribution/address/indexes/any.rs +++ b/crates/brk_computer/src/distribution/address/indexes/any.rs @@ -58,9 +58,10 @@ macro_rules! define_any_address_indexes_vecs { } /// Get address index for a given type and typeindex. + /// Uses get_any_or_read_at_unwrap to check updated layer (needed after rollback). pub fn get(&self, address_type: OutputType, typeindex: TypeIndex, reader: &Reader) -> AnyAddressIndex { match address_type { - $(OutputType::$variant => self.$field.get_pushed_or_read_at_unwrap(typeindex.into(), reader),)* + $(OutputType::$variant => self.$field.get_any_or_read_at_unwrap(typeindex.into(), reader),)* _ => unreachable!("Invalid address type: {:?}", address_type), } } diff --git a/crates/brk_computer/src/distribution/block/cache/address.rs b/crates/brk_computer/src/distribution/block/cache/address.rs index b5b6da32f..a333c6f1d 100644 --- a/crates/brk_computer/src/distribution/block/cache/address.rs +++ b/crates/brk_computer/src/distribution/block/cache/address.rs @@ -112,16 +112,18 @@ pub fn load_uncached_address_data( Some(match anyaddressindex.to_enum() { AnyAddressDataIndexEnum::Loaded(loaded_index) => { let reader = &vr.anyaddressindex_to_anyaddressdata.loaded; + // Use get_any_or_read_unwrap to check updated layer (needed after rollback) let loaded_data = addresses_data .loaded - .get_pushed_or_read_unwrap(loaded_index, reader); + .get_any_or_read_unwrap(loaded_index, reader); WithAddressDataSource::FromLoaded(loaded_index, loaded_data) } AnyAddressDataIndexEnum::Empty(empty_index) => { let reader = &vr.anyaddressindex_to_anyaddressdata.empty; + // Use get_any_or_read_unwrap to check updated layer (needed after rollback) let empty_data = addresses_data .empty - .get_pushed_or_read_unwrap(empty_index, reader); + .get_any_or_read_unwrap(empty_index, reader); WithAddressDataSource::FromEmpty(empty_index, empty_data.into()) } }) diff --git a/crates/brk_computer/src/distribution/compute/recover.rs b/crates/brk_computer/src/distribution/compute/recover.rs index 470dcfafe..ba6833e44 100644 --- a/crates/brk_computer/src/distribution/compute/recover.rs +++ b/crates/brk_computer/src/distribution/compute/recover.rs @@ -2,6 +2,7 @@ use std::{cmp::Ordering, collections::BTreeSet}; use brk_error::Result; use brk_types::Height; +use tracing::{debug, warn}; use vecdb::Stamp; use super::super::{ @@ -44,27 +45,49 @@ pub fn recover_state( // If rollbacks are inconsistent, start fresh if consistent_height.is_zero() { + warn!("Rollback consistency check failed: inconsistent heights"); return Ok(RecoveredState { starting_height: Height::ZERO, }); } + // Rollback can land at an earlier height (multi-block change file), which is fine. + // But if it lands AHEAD of target, that means rollback failed (missing change files). + if consistent_height > height { + warn!( + "Rollback failed: still at {} but target was {}, falling back to fresh start", + consistent_height, height + ); + return Ok(RecoveredState { + starting_height: Height::ZERO, + }); + } + + if consistent_height != height { + debug!( + "Rollback landed at {} instead of {}, will resume from there", + consistent_height, height + ); + } + // Import UTXO cohort states - all must succeed - if !utxo_cohorts.import_separate_states(height) { + if !utxo_cohorts.import_separate_states(consistent_height) { + warn!("UTXO cohort state import failed at height {}", consistent_height); return Ok(RecoveredState { starting_height: Height::ZERO, }); } // Import address cohort states - all must succeed - if !address_cohorts.import_separate_states(height) { + if !address_cohorts.import_separate_states(consistent_height) { + warn!("Address cohort state import failed at height {}", consistent_height); return Ok(RecoveredState { starting_height: Height::ZERO, }); } Ok(RecoveredState { - starting_height: height, + starting_height: consistent_height, }) } @@ -132,28 +155,38 @@ fn rollback_states( // All rollbacks must succeed - any error means fresh start let Ok(s) = chain_state_rollback else { + warn!("chain_state rollback failed: {:?}", chain_state_rollback); return Height::ZERO; }; - heights.insert(Height::from(s).incremented()); + let chain_height = Height::from(s).incremented(); + debug!("chain_state rolled back to stamp {:?}, height {}", s, chain_height); + heights.insert(chain_height); let Ok(stamps) = address_indexes_rollbacks else { + warn!("address_indexes rollback failed: {:?}", address_indexes_rollbacks); return Height::ZERO; }; - for s in stamps { - heights.insert(Height::from(s).incremented()); + for (i, s) in stamps.iter().enumerate() { + let h = Height::from(*s).incremented(); + debug!("address_indexes[{}] rolled back to stamp {:?}, height {}", i, s, h); + heights.insert(h); } let Ok(stamps) = address_data_rollbacks else { + warn!("address_data rollback failed: {:?}", address_data_rollbacks); return Height::ZERO; }; - for s in stamps { - heights.insert(Height::from(s).incremented()); + for (i, s) in stamps.iter().enumerate() { + let h = Height::from(*s).incremented(); + debug!("address_data[{}] rolled back to stamp {:?}, height {}", i, s, h); + heights.insert(h); } // All must agree on the same height if heights.len() == 1 { heights.pop_first().unwrap() } else { + warn!("Rollback heights inconsistent: {:?}", heights); Height::ZERO } } diff --git a/crates/brk_computer/src/distribution/state/cost_basis/unrealized.rs b/crates/brk_computer/src/distribution/state/cost_basis/unrealized.rs index e391dbf85..88a2d86f7 100644 --- a/crates/brk_computer/src/distribution/state/cost_basis/unrealized.rs +++ b/crates/brk_computer/src/distribution/state/cost_basis/unrealized.rs @@ -1,6 +1,6 @@ use std::ops::Bound; -use brk_types::{Dollars, Sats}; +use brk_types::{CentsUnsigned, Dollars, Sats}; use vecdb::CheckedSub; use super::price_to_amount::PriceToAmount; @@ -11,6 +11,10 @@ pub struct UnrealizedState { pub supply_in_loss: Sats, pub unrealized_profit: Dollars, pub unrealized_loss: Dollars, + /// Invested capital in profit: Σ(sats × price) where price <= spot + pub invested_capital_in_profit: Dollars, + /// Invested capital in loss: Σ(sats × price) where price > spot + pub invested_capital_in_loss: Dollars, } impl UnrealizedState { @@ -19,6 +23,8 @@ impl UnrealizedState { supply_in_loss: Sats::ZERO, unrealized_profit: Dollars::NAN, unrealized_loss: Dollars::NAN, + invested_capital_in_profit: Dollars::NAN, + invested_capital_in_loss: Dollars::NAN, }; pub const ZERO: Self = Self { @@ -26,6 +32,8 @@ impl UnrealizedState { supply_in_loss: Sats::ZERO, unrealized_profit: Dollars::ZERO, unrealized_loss: Dollars::ZERO, + invested_capital_in_profit: Dollars::ZERO, + invested_capital_in_loss: Dollars::ZERO, }; } @@ -62,14 +70,17 @@ impl CachedUnrealizedState { /// Update cached state when a receive happens. /// Determines profit/loss classification relative to cached price. pub fn on_receive(&mut self, purchase_price: Dollars, sats: Sats) { + let invested_capital = purchase_price * sats; if purchase_price <= self.at_price { self.state.supply_in_profit += sats; + self.state.invested_capital_in_profit += invested_capital; if purchase_price < self.at_price { let diff = self.at_price.checked_sub(purchase_price).unwrap(); self.state.unrealized_profit += diff * sats; } } else { self.state.supply_in_loss += sats; + self.state.invested_capital_in_loss += invested_capital; let diff = purchase_price.checked_sub(self.at_price).unwrap(); self.state.unrealized_loss += diff * sats; } @@ -77,9 +88,15 @@ impl CachedUnrealizedState { /// Update cached state when a send happens from historical price. pub fn on_send(&mut self, historical_price: Dollars, sats: Sats) { + let invested_capital = historical_price * sats; if historical_price <= self.at_price { // Was in profit self.state.supply_in_profit -= sats; + self.state.invested_capital_in_profit = self + .state + .invested_capital_in_profit + .checked_sub(invested_capital) + .unwrap(); if historical_price < self.at_price { let diff = self.at_price.checked_sub(historical_price).unwrap(); let profit_removed = diff * sats; @@ -92,6 +109,11 @@ impl CachedUnrealizedState { } else { // Was in loss self.state.supply_in_loss -= sats; + self.state.invested_capital_in_loss = self + .state + .invested_capital_in_loss + .checked_sub(invested_capital) + .unwrap(); let diff = historical_price.checked_sub(self.at_price).unwrap(); let loss_removed = diff * sats; self.state.unrealized_loss = self @@ -210,14 +232,17 @@ impl CachedUnrealizedState { let mut state = UnrealizedState::ZERO; for (price, &sats) in price_to_amount.iter() { + let invested_capital = price * sats; if price <= current_price { state.supply_in_profit += sats; + state.invested_capital_in_profit += invested_capital; if price < current_price { let diff = current_price.checked_sub(price).unwrap(); state.unrealized_profit += diff * sats; } } else { state.supply_in_loss += sats; + state.invested_capital_in_loss += invested_capital; let diff = price.checked_sub(current_price).unwrap(); state.unrealized_loss += diff * sats; } diff --git a/crates/brk_indexer/src/constants.rs b/crates/brk_indexer/src/constants.rs index 1f04bea41..60ca8f6df 100644 --- a/crates/brk_indexer/src/constants.rs +++ b/crates/brk_indexer/src/constants.rs @@ -1,12 +1,11 @@ use std::str::FromStr; -use brk_types::{Height, TxIndex, Txid, TxidPrefix, Version}; +use brk_types::{TxIndex, Txid, TxidPrefix, Version}; // One version for all data sources // Increment on **change _OR_ addition** pub const VERSION: Version = Version::new(24); pub const SNAPSHOT_BLOCK_RANGE: usize = 1_000; -pub const COLLISIONS_CHECKED_UP_TO: Height = Height::new(0); /// Known duplicate Bitcoin transactions (BIP30) /// https://github.com/bitcoin/bips/blob/master/bip-0030.mediawiki diff --git a/crates/brk_indexer/src/lib.rs b/crates/brk_indexer/src/lib.rs index 833b1ff28..f7dde779e 100644 --- a/crates/brk_indexer/src/lib.rs +++ b/crates/brk_indexer/src/lib.rs @@ -102,6 +102,15 @@ impl Indexer { let (starting_indexes, prev_hash) = if let Some(hash) = last_blockhash { let (height, hash) = client.get_closest_valid_height(hash)?; + // TEST: force rollback 5 blocks (only if we have enough blocks) + let (height, hash) = if *height > 10 { + let height = Height::from(height.checked_sub(1).unwrap()); + let hash = self.vecs.blocks.blockhash.iter()?.get(height).unwrap(); + (height, hash) + } else { + (height, hash) + }; + // END TEST match Indexes::from_vecs_and_stores(height.incremented(), &mut self.vecs, &self.stores) { Some(starting_indexes) => { @@ -177,13 +186,10 @@ impl Indexer { indexes.height = height; - // Used to check rapidhash collisions - let block_check_collisions = check_collisions && height > COLLISIONS_CHECKED_UP_TO; - let mut processor = BlockProcessor { block: &block, height, - check_collisions: block_check_collisions, + check_collisions, indexes: &mut indexes, vecs, stores, diff --git a/crates/brk_indexer/src/processor/tx.rs b/crates/brk_indexer/src/processor/tx.rs index 4ec0e446c..b50592494 100644 --- a/crates/brk_indexer/src/processor/tx.rs +++ b/crates/brk_indexer/src/processor/tx.rs @@ -78,6 +78,8 @@ impl<'a> BlockProcessor<'a> { pub fn store_transaction_metadata(&mut self, txs: Vec) -> Result<()> { let height = self.height; + let mut inserted = 0usize; + let mut skipped = 0usize; for ct in txs { if ct.prev_txindex_opt.is_none() { @@ -86,6 +88,9 @@ impl<'a> BlockProcessor<'a> { ct.txindex, height, ); + inserted += 1; + } else { + skipped += 1; } self.vecs @@ -118,6 +123,11 @@ impl<'a> BlockProcessor<'a> { .checked_push(ct.txindex, StoredBool::from(ct.tx.is_explicitly_rbf()))?; } + tracing::debug!( + "store_transaction_metadata: height={}, inserted={}, skipped={}", + height, inserted, skipped + ); + Ok(()) } } diff --git a/crates/brk_indexer/src/processor/txin.rs b/crates/brk_indexer/src/processor/txin.rs index 96902c783..4700ba788 100644 --- a/crates/brk_indexer/src/processor/txin.rs +++ b/crates/brk_indexer/src/processor/txin.rs @@ -81,6 +81,11 @@ impl<'a> BlockProcessor<'a> { { txindex } else { + let store_result = self.stores.txidprefix_to_txindex.get(&txid_prefix)?; + tracing::error!( + "UnknownTxid: txid={}, prefix={:?}, store_result={:?}, current_txindex={:?}", + txid, txid_prefix, store_result, self.indexes.txindex + ); return Err(Error::UnknownTxid); }; diff --git a/crates/brk_indexer/src/stores.rs b/crates/brk_indexer/src/stores.rs index a468af397..7bddff020 100644 --- a/crates/brk_indexer/src/stores.rs +++ b/crates/brk_indexer/src/stores.rs @@ -252,6 +252,14 @@ impl Stores { } if starting_indexes.txindex != TxIndex::ZERO { + let txid_vec_len = vecs.transactions.txid.len(); + let skip_count = starting_indexes.txindex.to_usize(); + let remove_count = txid_vec_len.saturating_sub(skip_count); + tracing::debug!( + "Rollback TXIDs: vec_len={}, skip={}, removing={}", + txid_vec_len, skip_count, remove_count + ); + vecs.transactions .txid .iter()? @@ -272,6 +280,9 @@ impl Stores { self.txidprefix_to_txindex.remove(txidprefix); } }); + + // Clear caches to prevent stale reads after rollback + self.txidprefix_to_txindex.clear_caches(); } else { unreachable!(); } diff --git a/crates/brk_query/src/impl/metrics.rs b/crates/brk_query/src/impl/metrics.rs index f2813d1ac..5bcb6f35a 100644 --- a/crates/brk_query/src/impl/metrics.rs +++ b/crates/brk_query/src/impl/metrics.rs @@ -156,6 +156,7 @@ impl Query { Ok(ResolvedQuery { vecs, format: params.format(), + index: params.index, version, total, start, @@ -170,6 +171,7 @@ impl Query { let ResolvedQuery { vecs, format, + index, version, total, start, @@ -182,7 +184,7 @@ impl Query { Format::JSON => { if vecs.len() == 1 { let mut buf = Vec::new(); - MetricData::serialize(vecs[0], start, end, &mut buf)?; + MetricData::serialize(vecs[0], index, start, end, &mut buf)?; Output::Json(buf) } else { let mut buf = Vec::new(); @@ -191,7 +193,7 @@ impl Query { if i > 0 { buf.push(b','); } - MetricData::serialize(*vec, start, end, &mut buf)?; + MetricData::serialize(*vec, index, start, end, &mut buf)?; } buf.push(b']'); Output::Json(buf) diff --git a/crates/brk_query/src/resolved.rs b/crates/brk_query/src/resolved.rs index 7db662178..c0eb27216 100644 --- a/crates/brk_query/src/resolved.rs +++ b/crates/brk_query/src/resolved.rs @@ -1,4 +1,4 @@ -use brk_types::{Etag, Format}; +use brk_types::{Etag, Format, Index}; use vecdb::AnyExportableVec; /// A resolved metric query ready for formatting. @@ -6,6 +6,7 @@ use vecdb::AnyExportableVec; pub struct ResolvedQuery { pub(crate) vecs: Vec<&'static dyn AnyExportableVec>, pub(crate) format: Format, + pub(crate) index: Index, pub(crate) version: u64, pub(crate) total: usize, pub(crate) start: usize, diff --git a/crates/brk_server/src/api/metrics/mod.rs b/crates/brk_server/src/api/metrics/mod.rs index 236b56645..2d910c7d5 100644 --- a/crates/brk_server/src/api/metrics/mod.rs +++ b/crates/brk_server/src/api/metrics/mod.rs @@ -23,14 +23,16 @@ mod legacy; /// Maximum allowed request weight in bytes (650KB) const MAX_WEIGHT: usize = 65 * 10_000; +/// Maximum allowed request weight for localhost (50MB) +const MAX_WEIGHT_LOCALHOST: usize = 50 * 1_000_000; /// Cache control header for metric data responses const CACHE_CONTROL: &str = "public, max-age=1, must-revalidate"; /// Returns the max weight for a request based on the client address. -/// Localhost requests have no weight limit. +/// Localhost requests get a generous limit, external requests get a stricter one. fn max_weight(addr: &SocketAddr) -> usize { if addr.ip().is_loopback() { - usize::MAX + MAX_WEIGHT_LOCALHOST } else { MAX_WEIGHT } diff --git a/crates/brk_store/src/lib.rs b/crates/brk_store/src/lib.rs index 99f525909..8d3902759 100644 --- a/crates/brk_store/src/lib.rs +++ b/crates/brk_store/src/lib.rs @@ -212,6 +212,14 @@ where } } + /// Clear all caches. Call after bulk removals (e.g., rollback) to prevent stale reads. + #[inline] + pub fn clear_caches(&mut self) { + for cache in &mut self.caches { + cache.clear(); + } + } + #[inline] pub fn iter(&self) -> impl Iterator { self.keyspace diff --git a/crates/brk_types/src/cents_signed.rs b/crates/brk_types/src/cents_signed.rs new file mode 100644 index 000000000..f0c8a762f --- /dev/null +++ b/crates/brk_types/src/cents_signed.rs @@ -0,0 +1,236 @@ +use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign}; + +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use vecdb::{Formattable, Pco}; + +use super::Dollars; + +/// Signed cents (i64) - for values that can be negative. +/// Used for profit/loss calculations, deltas, etc. +#[derive( + Debug, + Default, + Clone, + Copy, + PartialEq, + Eq, + PartialOrd, + Ord, + Serialize, + Deserialize, + Pco, + JsonSchema, +)] +pub struct CentsSigned(i64); + +impl CentsSigned { + pub const ZERO: Self = Self(0); + + #[inline] + pub const fn new(value: i64) -> Self { + Self(value) + } + + #[inline] + pub const fn inner(self) -> i64 { + self.0 + } + + #[inline] + pub fn is_negative(self) -> bool { + self.0 < 0 + } + + #[inline] + pub fn checked_sub(self, rhs: Self) -> Option { + self.0.checked_sub(rhs.0).map(Self) + } + + #[inline] + pub fn checked_add(self, rhs: Self) -> Option { + self.0.checked_add(rhs.0).map(Self) + } + + pub fn to_dollars(self) -> Dollars { + Dollars::from(self.0 as f64 / 100.0) + } + + pub fn round_to(self, digits: i32) -> Self { + let v = self.0; + let ilog10 = v.unsigned_abs().checked_ilog10().unwrap_or(0) as i32; + Self::from(if ilog10 >= digits { + let log_diff = ilog10 - digits + 1; + let pow = 10.0_f64.powi(log_diff); + ((v as f64 / pow).round() * pow) as i64 + } else { + v + }) + } +} + +impl From for CentsSigned { + #[inline] + fn from(value: Dollars) -> Self { + Self((*value * 100.0).round() as i64) + } +} + +impl From for Dollars { + #[inline] + fn from(value: CentsSigned) -> Self { + value.to_dollars() + } +} + +impl From for f64 { + #[inline] + fn from(value: CentsSigned) -> Self { + value.0 as f64 + } +} + +impl From for CentsSigned { + #[inline] + fn from(value: i64) -> Self { + Self(value) + } +} + +impl From for CentsSigned { + #[inline] + fn from(value: u64) -> Self { + Self(value as i64) + } +} + +impl From for usize { + #[inline] + fn from(value: CentsSigned) -> Self { + debug_assert!(value.0 >= 0, "Cannot convert negative CentsSigned to usize"); + value.0 as usize + } +} + +impl From for CentsSigned { + #[inline] + fn from(value: usize) -> Self { + Self(value as i64) + } +} + +impl From for i64 { + #[inline] + fn from(value: CentsSigned) -> Self { + value.0 + } +} + +impl From for u64 { + #[inline] + fn from(value: CentsSigned) -> Self { + debug_assert!(value.0 >= 0, "Cannot convert negative CentsSigned to u64"); + value.0 as u64 + } +} + +impl Add for CentsSigned { + type Output = Self; + #[inline] + fn add(self, rhs: Self) -> Self::Output { + Self(self.0 + rhs.0) + } +} + +impl AddAssign for CentsSigned { + #[inline] + fn add_assign(&mut self, rhs: Self) { + self.0 += rhs.0; + } +} + +impl Sub for CentsSigned { + type Output = Self; + #[inline] + fn sub(self, rhs: Self) -> Self::Output { + Self(self.0 - rhs.0) + } +} + +impl SubAssign for CentsSigned { + #[inline] + fn sub_assign(&mut self, rhs: Self) { + self.0 -= rhs.0; + } +} + +impl Div for CentsSigned { + type Output = Self; + #[inline] + fn div(self, rhs: Self) -> Self::Output { + Self(self.0 / rhs.0) + } +} + +impl Div for CentsSigned { + type Output = Self; + #[inline] + fn div(self, rhs: usize) -> Self::Output { + Self(self.0 / rhs as i64) + } +} + +impl From for CentsSigned { + #[inline] + fn from(value: u128) -> Self { + debug_assert!(value <= i64::MAX as u128, "u128 overflow to CentsSigned"); + Self(value as i64) + } +} + +impl From for u128 { + #[inline] + fn from(value: CentsSigned) -> Self { + debug_assert!(value.0 >= 0, "Cannot convert negative CentsSigned to u128"); + value.0 as u128 + } +} + +impl Mul for CentsSigned { + type Output = CentsSigned; + #[inline] + fn mul(self, rhs: CentsSigned) -> Self::Output { + Self(self.0.checked_mul(rhs.0).expect("CentsSigned overflow")) + } +} + +impl Mul for CentsSigned { + type Output = CentsSigned; + #[inline] + fn mul(self, rhs: i64) -> Self::Output { + Self(self.0 * rhs) + } +} + +impl Mul for CentsSigned { + type Output = CentsSigned; + #[inline] + fn mul(self, rhs: usize) -> Self::Output { + Self(self.0 * rhs as i64) + } +} + +impl std::fmt::Display for CentsSigned { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut buf = itoa::Buffer::new(); + let str = buf.format(self.0); + f.write_str(str) + } +} + +impl Formattable for CentsSigned { + #[inline(always)] + fn may_need_escaping() -> bool { + false + } +} diff --git a/crates/brk_types/src/cents_signed_compact.rs b/crates/brk_types/src/cents_signed_compact.rs new file mode 100644 index 000000000..2b5b9df38 --- /dev/null +++ b/crates/brk_types/src/cents_signed_compact.rs @@ -0,0 +1,91 @@ +use serde::{Deserialize, Serialize}; + +use super::Dollars; + +/// Compact signed cents (i32) - memory-efficient for map keys. +/// Supports prices from -$21,474,836.47 to $21,474,836.47 (i32 range / 100). +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub struct CentsSignedCompact(i32); + +impl CentsSignedCompact { + pub const ZERO: Self = Self(0); + + #[inline] + pub const fn new(value: i32) -> Self { + Self(value) + } + + #[inline] + pub const fn inner(self) -> i32 { + self.0 + } + + #[inline] + pub fn is_negative(self) -> bool { + self.0 < 0 + } + + #[inline] + pub fn to_dollars(self) -> Dollars { + Dollars::from(self.0 as f64 / 100.0) + } + + #[inline] + pub fn checked_sub(self, rhs: Self) -> Option { + self.0.checked_sub(rhs.0).map(Self) + } +} + +impl From for CentsSignedCompact { + #[inline] + fn from(value: Dollars) -> Self { + let f = f64::from(value); + if f.is_nan() { + Self::ZERO + } else { + let cents = (f * 100.0).round(); + debug_assert!( + cents >= i32::MIN as f64 && cents <= i32::MAX as f64, + "Price ${} exceeds CentsSignedCompact range (~$21.5M)", + f + ); + Self(cents as i32) + } + } +} + +impl From for Dollars { + #[inline] + fn from(value: CentsSignedCompact) -> Self { + value.to_dollars() + } +} + +impl From for CentsSignedCompact { + #[inline] + fn from(value: i32) -> Self { + Self(value) + } +} + +impl From for i32 { + #[inline] + fn from(value: CentsSignedCompact) -> Self { + value.0 + } +} + +impl From for f64 { + #[inline] + fn from(value: CentsSignedCompact) -> Self { + value.0 as f64 + } +} + +impl std::fmt::Display for CentsSignedCompact { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut buf = itoa::Buffer::new(); + let str = buf.format(self.0); + f.write_str(str) + } +} diff --git a/crates/brk_types/src/cents_unsigned.rs b/crates/brk_types/src/cents_unsigned.rs new file mode 100644 index 000000000..f25b81ceb --- /dev/null +++ b/crates/brk_types/src/cents_unsigned.rs @@ -0,0 +1,207 @@ +use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign}; + +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use vecdb::{Formattable, Pco}; + +use super::Dollars; + +/// Unsigned cents (u64) - for values that should never be negative. +/// Used for invested capital, realized cap, etc. +#[derive( + Debug, + Default, + Clone, + Copy, + PartialEq, + Eq, + PartialOrd, + Ord, + Serialize, + Deserialize, + Pco, + JsonSchema, +)] +pub struct CentsUnsigned(u64); + +impl CentsUnsigned { + pub const ZERO: Self = Self(0); + pub const MAX: Self = Self(u64::MAX); + + #[inline] + pub const fn new(value: u64) -> Self { + Self(value) + } + + #[inline] + pub const fn inner(self) -> u64 { + self.0 + } + + #[inline] + pub fn checked_sub(self, rhs: Self) -> Option { + self.0.checked_sub(rhs.0).map(Self) + } + + #[inline] + pub fn saturating_sub(self, rhs: Self) -> Self { + Self(self.0.saturating_sub(rhs.0)) + } + + #[inline] + pub fn checked_add(self, rhs: Self) -> Option { + self.0.checked_add(rhs.0).map(Self) + } + + pub fn to_dollars(self) -> Dollars { + Dollars::from(self.0 as f64 / 100.0) + } +} + +impl From for CentsUnsigned { + #[inline] + fn from(value: Dollars) -> Self { + let f = f64::from(value); + if f.is_nan() || f < 0.0 { + Self::ZERO + } else { + Self((f * 100.0).round() as u64) + } + } +} + +impl From for Dollars { + #[inline] + fn from(value: CentsUnsigned) -> Self { + value.to_dollars() + } +} + +impl From for CentsUnsigned { + #[inline] + fn from(value: u64) -> Self { + Self(value) + } +} + +impl From for u64 { + #[inline] + fn from(value: CentsUnsigned) -> Self { + value.0 + } +} + +impl From for CentsUnsigned { + #[inline] + fn from(value: u128) -> Self { + debug_assert!(value <= u64::MAX as u128, "u128 overflow to CentsUnsigned"); + Self(value as u64) + } +} + +impl From for u128 { + #[inline] + fn from(value: CentsUnsigned) -> Self { + value.0 as u128 + } +} + +impl From for f64 { + #[inline] + fn from(value: CentsUnsigned) -> Self { + value.0 as f64 + } +} + +impl Add for CentsUnsigned { + type Output = Self; + #[inline] + fn add(self, rhs: Self) -> Self::Output { + Self(self.0 + rhs.0) + } +} + +impl AddAssign for CentsUnsigned { + #[inline] + fn add_assign(&mut self, rhs: Self) { + self.0 += rhs.0; + } +} + +impl Sub for CentsUnsigned { + type Output = Self; + #[inline] + fn sub(self, rhs: Self) -> Self::Output { + Self(self.0 - rhs.0) + } +} + +impl SubAssign for CentsUnsigned { + #[inline] + fn sub_assign(&mut self, rhs: Self) { + self.0 -= rhs.0; + } +} + +impl Mul for CentsUnsigned { + type Output = Self; + #[inline] + fn mul(self, rhs: Self) -> Self::Output { + Self(self.0.checked_mul(rhs.0).expect("CentsUnsigned overflow")) + } +} + +impl Mul for CentsUnsigned { + type Output = Self; + #[inline] + fn mul(self, rhs: u64) -> Self::Output { + Self(self.0 * rhs) + } +} + +impl Mul for CentsUnsigned { + type Output = Self; + #[inline] + fn mul(self, rhs: usize) -> Self::Output { + Self(self.0 * rhs as u64) + } +} + +impl Div for CentsUnsigned { + type Output = Self; + #[inline] + fn div(self, rhs: Self) -> Self::Output { + Self(self.0 / rhs.0) + } +} + +impl Div for CentsUnsigned { + type Output = Self; + #[inline] + fn div(self, rhs: u64) -> Self::Output { + Self(self.0 / rhs) + } +} + +impl Div for CentsUnsigned { + type Output = Self; + #[inline] + fn div(self, rhs: usize) -> Self::Output { + Self(self.0 / rhs as u64) + } +} + +impl std::fmt::Display for CentsUnsigned { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut buf = itoa::Buffer::new(); + let str = buf.format(self.0); + f.write_str(str) + } +} + +impl Formattable for CentsUnsigned { + #[inline(always)] + fn may_need_escaping() -> bool { + false + } +} diff --git a/crates/brk_types/src/cents_unsigned_compact.rs b/crates/brk_types/src/cents_unsigned_compact.rs new file mode 100644 index 000000000..045f7d6ed --- /dev/null +++ b/crates/brk_types/src/cents_unsigned_compact.rs @@ -0,0 +1,92 @@ +use serde::{Deserialize, Serialize}; + +use super::Dollars; + +/// Compact unsigned cents (u32) - memory-efficient for map keys. +/// Supports values from $0.00 to $42,949,672.95 (u32::MAX / 100). +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub struct CentsUnsignedCompact(u32); + +impl CentsUnsignedCompact { + pub const ZERO: Self = Self(0); + pub const MAX: Self = Self(u32::MAX); + + #[inline] + pub const fn new(value: u32) -> Self { + Self(value) + } + + #[inline] + pub const fn inner(self) -> u32 { + self.0 + } + + #[inline] + pub fn to_dollars(self) -> Dollars { + Dollars::from(self.0 as f64 / 100.0) + } + + #[inline] + pub fn checked_sub(self, rhs: Self) -> Option { + self.0.checked_sub(rhs.0).map(Self) + } + + #[inline] + pub fn saturating_sub(self, rhs: Self) -> Self { + Self(self.0.saturating_sub(rhs.0)) + } +} + +impl From for CentsUnsignedCompact { + #[inline] + fn from(value: Dollars) -> Self { + let f = f64::from(value); + if f.is_nan() || f < 0.0 { + Self::ZERO + } else { + let cents = (f * 100.0).round(); + debug_assert!( + cents <= u32::MAX as f64, + "Price ${} exceeds CentsUnsignedCompact max (~$42.9M)", + f + ); + Self(cents as u32) + } + } +} + +impl From for Dollars { + #[inline] + fn from(value: CentsUnsignedCompact) -> Self { + value.to_dollars() + } +} + +impl From for CentsUnsignedCompact { + #[inline] + fn from(value: u32) -> Self { + Self(value) + } +} + +impl From for u32 { + #[inline] + fn from(value: CentsUnsignedCompact) -> Self { + value.0 + } +} + +impl From for f64 { + #[inline] + fn from(value: CentsUnsignedCompact) -> Self { + value.0 as f64 + } +} + +impl std::fmt::Display for CentsUnsignedCompact { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut buf = itoa::Buffer::new(); + let str = buf.format(self.0); + f.write_str(str) + } +} diff --git a/crates/brk_types/src/date.rs b/crates/brk_types/src/date.rs index b5ef36a22..bfeed252f 100644 --- a/crates/brk_types/src/date.rs +++ b/crates/brk_types/src/date.rs @@ -7,7 +7,7 @@ use vecdb::{Formattable, Pco}; use crate::ONE_DAY_IN_SEC_F64; -use super::{DateIndex, Timestamp}; +use super::{DateIndex, DecadeIndex, MonthIndex, QuarterIndex, SemesterIndex, Timestamp, WeekIndex, YearIndex}; /// Date in YYYYMMDD format stored as u32 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Pco, JsonSchema)] @@ -111,6 +111,72 @@ impl From for Date { } } +impl From for Date { + #[inline] + fn from(value: WeekIndex) -> Self { + // Week 0 starts at genesis (2009-01-03), add i weeks + Self::from( + Self::INDEX_ZERO_ + .checked_add(Span::new().weeks(i64::from(u16::from(value)))) + .unwrap(), + ) + } +} + +impl From for Date { + #[inline] + fn from(value: MonthIndex) -> Self { + // Month 0 is January 2009, add i months + Self::from( + Date_::constant(2009, 1, 1) + .checked_add(Span::new().months(i64::from(u16::from(value)))) + .unwrap(), + ) + } +} + +impl From for Date { + #[inline] + fn from(value: YearIndex) -> Self { + // Year 0 is 2009 + let year = 2009i16 + usize::from(value) as i16; + Self::from(Date_::constant(year, 1, 1)) + } +} + +impl From for Date { + #[inline] + fn from(value: QuarterIndex) -> Self { + // Quarter 0 is Q1 2009, add i*3 months + Self::from( + Date_::constant(2009, 1, 1) + .checked_add(Span::new().months(usize::from(value) as i64 * 3)) + .unwrap(), + ) + } +} + +impl From for Date { + #[inline] + fn from(value: SemesterIndex) -> Self { + // Semester 0 is H1 2009, add i*6 months + Self::from( + Date_::constant(2009, 1, 1) + .checked_add(Span::new().months(usize::from(value) as i64 * 6)) + .unwrap(), + ) + } +} + +impl From for Date { + #[inline] + fn from(value: DecadeIndex) -> Self { + // Decade 0 is 2009, add i*10 years + let year = 2009i16 + usize::from(value) as i16 * 10; + Self::from(Date_::constant(year, 1, 1)) + } +} + impl Serialize for Date { fn serialize(&self, serializer: S) -> Result where @@ -190,3 +256,172 @@ impl Formattable for Date { false } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_date_from_dateindex_zero() { + // DateIndex 0 is genesis: Jan 3, 2009 + let date = Date::from(DateIndex::from(0_usize)); + assert_eq!(date, Date::INDEX_ZERO); + assert_eq!(date.year(), 2009); + assert_eq!(date.month(), 1); + assert_eq!(date.day(), 3); + } + + #[test] + fn test_date_from_dateindex_one() { + // DateIndex 1 is Jan 9, 2009 (6 day gap after genesis) + let date = Date::from(DateIndex::from(1_usize)); + assert_eq!(date, Date::INDEX_ONE); + assert_eq!(date.year(), 2009); + assert_eq!(date.month(), 1); + assert_eq!(date.day(), 9); + } + + #[test] + fn test_date_from_dateindex_two() { + // DateIndex 2 is Jan 10, 2009 + let date = Date::from(DateIndex::from(2_usize)); + assert_eq!(date.year(), 2009); + assert_eq!(date.month(), 1); + assert_eq!(date.day(), 10); + } + + #[test] + fn test_date_from_weekindex_zero() { + // WeekIndex 0 starts at genesis: Jan 3, 2009 + let date = Date::from(WeekIndex::from(0_usize)); + assert_eq!(date.year(), 2009); + assert_eq!(date.month(), 1); + assert_eq!(date.day(), 3); + } + + #[test] + fn test_date_from_weekindex_one() { + // WeekIndex 1 is Jan 10, 2009 (one week after genesis) + let date = Date::from(WeekIndex::from(1_usize)); + assert_eq!(date.year(), 2009); + assert_eq!(date.month(), 1); + assert_eq!(date.day(), 10); + } + + #[test] + fn test_date_from_monthindex_zero() { + // MonthIndex 0 is Jan 1, 2009 + let date = Date::from(MonthIndex::from(0_usize)); + assert_eq!(date.year(), 2009); + assert_eq!(date.month(), 1); + assert_eq!(date.day(), 1); + } + + #[test] + fn test_date_from_monthindex_one() { + // MonthIndex 1 is Feb 1, 2009 + let date = Date::from(MonthIndex::from(1_usize)); + assert_eq!(date.year(), 2009); + assert_eq!(date.month(), 2); + assert_eq!(date.day(), 1); + } + + #[test] + fn test_date_from_monthindex_twelve() { + // MonthIndex 12 is Jan 1, 2010 + let date = Date::from(MonthIndex::from(12_usize)); + assert_eq!(date.year(), 2010); + assert_eq!(date.month(), 1); + assert_eq!(date.day(), 1); + } + + #[test] + fn test_date_from_yearindex_zero() { + // YearIndex 0 is Jan 1, 2009 + let date = Date::from(YearIndex::from(0_usize)); + assert_eq!(date.year(), 2009); + assert_eq!(date.month(), 1); + assert_eq!(date.day(), 1); + } + + #[test] + fn test_date_from_yearindex_one() { + // YearIndex 1 is Jan 1, 2010 + let date = Date::from(YearIndex::from(1_usize)); + assert_eq!(date.year(), 2010); + assert_eq!(date.month(), 1); + assert_eq!(date.day(), 1); + } + + #[test] + fn test_date_from_quarterindex_zero() { + // QuarterIndex 0 is Q1 2009: Jan 1, 2009 + let date = Date::from(QuarterIndex::from(0_usize)); + assert_eq!(date.year(), 2009); + assert_eq!(date.month(), 1); + assert_eq!(date.day(), 1); + } + + #[test] + fn test_date_from_quarterindex_one() { + // QuarterIndex 1 is Q2 2009: Apr 1, 2009 + let date = Date::from(QuarterIndex::from(1_usize)); + assert_eq!(date.year(), 2009); + assert_eq!(date.month(), 4); + assert_eq!(date.day(), 1); + } + + #[test] + fn test_date_from_quarterindex_four() { + // QuarterIndex 4 is Q1 2010: Jan 1, 2010 + let date = Date::from(QuarterIndex::from(4_usize)); + assert_eq!(date.year(), 2010); + assert_eq!(date.month(), 1); + assert_eq!(date.day(), 1); + } + + #[test] + fn test_date_from_semesterindex_zero() { + // SemesterIndex 0 is H1 2009: Jan 1, 2009 + let date = Date::from(SemesterIndex::from(0_usize)); + assert_eq!(date.year(), 2009); + assert_eq!(date.month(), 1); + assert_eq!(date.day(), 1); + } + + #[test] + fn test_date_from_semesterindex_one() { + // SemesterIndex 1 is H2 2009: Jul 1, 2009 + let date = Date::from(SemesterIndex::from(1_usize)); + assert_eq!(date.year(), 2009); + assert_eq!(date.month(), 7); + assert_eq!(date.day(), 1); + } + + #[test] + fn test_date_from_semesterindex_two() { + // SemesterIndex 2 is H1 2010: Jan 1, 2010 + let date = Date::from(SemesterIndex::from(2_usize)); + assert_eq!(date.year(), 2010); + assert_eq!(date.month(), 1); + assert_eq!(date.day(), 1); + } + + #[test] + fn test_date_from_decadeindex_zero() { + // DecadeIndex 0 is 2009: Jan 1, 2009 + let date = Date::from(DecadeIndex::from(0_usize)); + assert_eq!(date.year(), 2009); + assert_eq!(date.month(), 1); + assert_eq!(date.day(), 1); + } + + #[test] + fn test_date_from_decadeindex_one() { + // DecadeIndex 1 is 2019: Jan 1, 2019 + let date = Date::from(DecadeIndex::from(1_usize)); + assert_eq!(date.year(), 2019); + assert_eq!(date.month(), 1); + assert_eq!(date.day(), 1); + } +} diff --git a/crates/brk_types/src/index.rs b/crates/brk_types/src/index.rs index b7da171e5..c58ee74aa 100644 --- a/crates/brk_types/src/index.rs +++ b/crates/brk_types/src/index.rs @@ -8,7 +8,7 @@ use vecdb::PrintableIndex; use crate::PairOutputIndex; use super::{ - DateIndex, DecadeIndex, DifficultyEpoch, EmptyAddressIndex, EmptyOutputIndex, HalvingEpoch, + Date, DateIndex, DecadeIndex, DifficultyEpoch, EmptyAddressIndex, EmptyOutputIndex, HalvingEpoch, Height, LoadedAddressIndex, MonthIndex, OpReturnIndex, P2AAddressIndex, P2MSOutputIndex, P2PK33AddressIndex, P2PK65AddressIndex, P2PKHAddressIndex, P2SHAddressIndex, P2TRAddressIndex, P2WPKHAddressIndex, P2WSHAddressIndex, QuarterIndex, SemesterIndex, TxInIndex, TxIndex, @@ -144,6 +144,35 @@ impl Index { _ => 1, } } + + /// Returns true if this index type is date-based. + pub const fn is_date_based(&self) -> bool { + matches!( + self, + Self::DateIndex + | Self::WeekIndex + | Self::MonthIndex + | Self::YearIndex + | Self::QuarterIndex + | Self::SemesterIndex + | Self::DecadeIndex + ) + } + + /// Convert an index value to a date for date-based indexes. + /// Returns None for non-date-based indexes. + pub fn index_to_date(&self, i: usize) -> Option { + match self { + Self::DateIndex => Some(Date::from(DateIndex::from(i))), + Self::WeekIndex => Some(Date::from(WeekIndex::from(i))), + Self::MonthIndex => Some(Date::from(MonthIndex::from(i))), + Self::YearIndex => Some(Date::from(YearIndex::from(i))), + Self::QuarterIndex => Some(Date::from(QuarterIndex::from(i))), + Self::SemesterIndex => Some(Date::from(SemesterIndex::from(i))), + Self::DecadeIndex => Some(Date::from(DecadeIndex::from(i))), + _ => None, + } + } } impl TryFrom<&str> for Index { @@ -219,3 +248,111 @@ impl<'de> Deserialize<'de> for Index { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_is_date_based_dateindex() { + assert!(Index::DateIndex.is_date_based()); + } + + #[test] + fn test_is_date_based_weekindex() { + assert!(Index::WeekIndex.is_date_based()); + } + + #[test] + fn test_is_date_based_monthindex() { + assert!(Index::MonthIndex.is_date_based()); + } + + #[test] + fn test_is_date_based_yearindex() { + assert!(Index::YearIndex.is_date_based()); + } + + #[test] + fn test_is_date_based_quarterindex() { + assert!(Index::QuarterIndex.is_date_based()); + } + + #[test] + fn test_is_date_based_semesterindex() { + assert!(Index::SemesterIndex.is_date_based()); + } + + #[test] + fn test_is_date_based_decadeindex() { + assert!(Index::DecadeIndex.is_date_based()); + } + + #[test] + fn test_is_not_date_based_height() { + assert!(!Index::Height.is_date_based()); + } + + #[test] + fn test_is_not_date_based_txindex() { + assert!(!Index::TxIndex.is_date_based()); + } + + #[test] + fn test_index_to_date_dateindex_zero() { + let date = Index::DateIndex.index_to_date(0).unwrap(); + assert_eq!(date, Date::from(DateIndex::from(0_usize))); + } + + #[test] + fn test_index_to_date_dateindex_one() { + let date = Index::DateIndex.index_to_date(1).unwrap(); + assert_eq!(date, Date::from(DateIndex::from(1_usize))); + } + + #[test] + fn test_index_to_date_weekindex() { + let date = Index::WeekIndex.index_to_date(1).unwrap(); + assert_eq!(date, Date::from(WeekIndex::from(1_usize))); + } + + #[test] + fn test_index_to_date_monthindex() { + let date = Index::MonthIndex.index_to_date(12).unwrap(); + assert_eq!(date, Date::from(MonthIndex::from(12_usize))); + } + + #[test] + fn test_index_to_date_yearindex() { + let date = Index::YearIndex.index_to_date(5).unwrap(); + assert_eq!(date, Date::from(YearIndex::from(5_usize))); + } + + #[test] + fn test_index_to_date_quarterindex() { + let date = Index::QuarterIndex.index_to_date(4).unwrap(); + assert_eq!(date, Date::from(QuarterIndex::from(4_usize))); + } + + #[test] + fn test_index_to_date_semesterindex() { + let date = Index::SemesterIndex.index_to_date(2).unwrap(); + assert_eq!(date, Date::from(SemesterIndex::from(2_usize))); + } + + #[test] + fn test_index_to_date_decadeindex() { + let date = Index::DecadeIndex.index_to_date(1).unwrap(); + assert_eq!(date, Date::from(DecadeIndex::from(1_usize))); + } + + #[test] + fn test_index_to_date_height_returns_none() { + assert!(Index::Height.index_to_date(100).is_none()); + } + + #[test] + fn test_index_to_date_txindex_returns_none() { + assert!(Index::TxIndex.index_to_date(100).is_none()); + } +} diff --git a/crates/brk_types/src/lib.rs b/crates/brk_types/src/lib.rs index 579749266..12619bb96 100644 --- a/crates/brk_types/src/lib.rs +++ b/crates/brk_types/src/lib.rs @@ -37,6 +37,10 @@ mod blockweightentry; mod bytes; mod cents; mod centscompact; +mod cents_signed; +mod cents_signed_compact; +mod cents_unsigned; +mod cents_unsigned_compact; mod datarange; mod datarangeformat; mod date; @@ -209,6 +213,10 @@ pub use blockweightentry::*; pub use bytes::*; pub use cents::*; pub use centscompact::*; +pub use cents_signed::*; +pub use cents_signed_compact::*; +pub use cents_unsigned::*; +pub use cents_unsigned_compact::*; pub use datarange::*; pub use datarangeformat::*; pub use date::*; diff --git a/crates/brk_types/src/metricdata.rs b/crates/brk_types/src/metricdata.rs index 579fb283d..d21957571 100644 --- a/crates/brk_types/src/metricdata.rs +++ b/crates/brk_types/src/metricdata.rs @@ -5,7 +5,7 @@ use serde::Deserialize; use serde_json::Value; use vecdb::AnySerializableVec; -use super::Timestamp; +use super::{Index, Timestamp}; /// Metric data with range information. /// @@ -15,6 +15,8 @@ use super::Timestamp; pub struct MetricData { /// Version of the metric data pub version: u64, + /// The index type used for this query + pub index: Index, /// Total number of data points in the metric pub total: usize, /// Start index (inclusive) of the returned range @@ -28,14 +30,16 @@ pub struct MetricData { } impl MetricData { - /// Write metric data as JSON to buffer: `{"version":N,"total":N,"start":N,"end":N,"stamp":"...","data":[...]}` + /// Write metric data as JSON to buffer: `{"version":N,"index":"...","total":N,"start":N,"end":N,"stamp":"...","data":[...]}` pub fn serialize( vec: &dyn AnySerializableVec, + index: Index, start: usize, end: usize, buf: &mut Vec, ) -> vecdb::Result<()> { let version = u64::from(vec.version()); + let index_str = index.serialize_long(); let total = vec.len(); let end = end.min(total); let start = start.min(end); @@ -43,10 +47,162 @@ impl MetricData { write!( buf, - r#"{{"version":{version},"total":{total},"start":{start},"end":{end},"stamp":"{stamp}","data":"#, + r#"{{"version":{version},"index":"{index_str}","total":{total},"start":{start},"end":{end},"stamp":"{stamp}","data":"#, )?; vec.write_json(Some(start), Some(end), buf)?; buf.push(b'}'); Ok(()) } } + +impl MetricData { + /// Returns an iterator over the index range. + pub fn indexes(&self) -> std::ops::Range { + self.start..self.end + } + + /// Returns true if this metric uses a date-based index. + pub fn is_date_based(&self) -> bool { + self.index.is_date_based() + } + + /// Returns an iterator over dates for the index range. + /// Panics if the index is not date-based. + pub fn dates(&self) -> impl Iterator + '_ { + let index = self.index; + self.indexes().map(move |i| { + index + .index_to_date(i) + .expect("dates() called on non-date-based index") + }) + } + + /// Iterate over (index, &value) pairs. + pub fn iter(&self) -> impl Iterator { + self.indexes().zip(self.data.iter()) + } + + /// Iterate over (date, &value) pairs. + /// Panics if the index is not date-based. + pub fn iter_dates(&self) -> impl Iterator + '_ { + self.dates().zip(self.data.iter()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn date_based_metric() -> MetricData { + MetricData { + version: 1, + index: Index::DateIndex, + total: 100, + start: 0, + end: 5, + stamp: "2024-01-01T00:00:00Z".to_string(), + data: vec![100, 200, 300, 400, 500], + } + } + + fn height_based_metric() -> MetricData { + MetricData { + version: 1, + index: Index::Height, + total: 1000, + start: 800000, + end: 800005, + stamp: "2024-01-01T00:00:00Z".to_string(), + data: vec![1.5, 2.5, 3.5, 4.5, 5.5], + } + } + + #[test] + fn test_indexes_returns_range() { + let metric = date_based_metric(); + let indexes: Vec<_> = metric.indexes().collect(); + assert_eq!(indexes, vec![0, 1, 2, 3, 4]); + } + + #[test] + fn test_indexes_with_offset() { + let metric = height_based_metric(); + let indexes: Vec<_> = metric.indexes().collect(); + assert_eq!(indexes, vec![800000, 800001, 800002, 800003, 800004]); + } + + #[test] + fn test_is_date_based_true() { + let metric = date_based_metric(); + assert!(metric.is_date_based()); + } + + #[test] + fn test_is_date_based_false() { + let metric = height_based_metric(); + assert!(!metric.is_date_based()); + } + + #[test] + fn test_dates_for_dateindex() { + let metric = date_based_metric(); + let dates: Vec<_> = metric.dates().collect(); + assert_eq!(dates.len(), 5); + // DateIndex 0 = Jan 3, 2009 (genesis) + assert_eq!(dates[0].year(), 2009); + assert_eq!(dates[0].month(), 1); + assert_eq!(dates[0].day(), 3); + // DateIndex 1 = Jan 9, 2009 (day one) + assert_eq!(dates[1].year(), 2009); + assert_eq!(dates[1].month(), 1); + assert_eq!(dates[1].day(), 9); + } + + #[test] + fn test_iter() { + let metric = date_based_metric(); + let pairs: Vec<_> = metric.iter().collect(); + assert_eq!(pairs.len(), 5); + assert_eq!(pairs[0], (0, &100)); + assert_eq!(pairs[1], (1, &200)); + assert_eq!(pairs[4], (4, &500)); + } + + #[test] + fn test_iter_with_offset() { + let metric = height_based_metric(); + let pairs: Vec<_> = metric.iter().collect(); + assert_eq!(pairs.len(), 5); + assert_eq!(pairs[0], (800000, &1.5)); + assert_eq!(pairs[4], (800004, &5.5)); + } + + #[test] + fn test_iter_dates() { + let metric = date_based_metric(); + let pairs: Vec<_> = metric.iter_dates().collect(); + assert_eq!(pairs.len(), 5); + // First pair: (Jan 3 2009, 100) + assert_eq!(pairs[0].0.year(), 2009); + assert_eq!(pairs[0].0.month(), 1); + assert_eq!(pairs[0].0.day(), 3); + assert_eq!(pairs[0].1, &100); + // Second pair: (Jan 9 2009, 200) + assert_eq!(pairs[1].0.day(), 9); + assert_eq!(pairs[1].1, &200); + } + + #[test] + #[should_panic(expected = "dates() called on non-date-based index")] + fn test_dates_panics_for_non_date_index() { + let metric = height_based_metric(); + let _: Vec<_> = metric.dates().collect(); + } + + #[test] + #[should_panic(expected = "dates() called on non-date-based index")] + fn test_iter_dates_panics_for_non_date_index() { + let metric = height_based_metric(); + let _: Vec<_> = metric.iter_dates().collect(); + } +} diff --git a/modules/.gitignore b/modules/.gitignore index 86aeefbd1..46ff68222 100644 --- a/modules/.gitignore +++ b/modules/.gitignore @@ -12,4 +12,3 @@ worker.* *.mts *.cts *.rs -*.md diff --git a/modules/brk-client/docs/interfaces/0sdUsdPattern.md b/modules/brk-client/docs/interfaces/0sdUsdPattern.md new file mode 100644 index 000000000..560675970 --- /dev/null +++ b/modules/brk-client/docs/interfaces/0sdUsdPattern.md @@ -0,0 +1,25 @@ +[**brk-client**](../README.md) + +*** + +[brk-client](../globals.md) / \_0sdUsdPattern + +# Interface: \_0sdUsdPattern + +Defined in: [Developer/brk/modules/brk-client/index.js:2763](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2763) + +## Properties + +### dollars + +> **dollars**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:2764](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2764) + +*** + +### sats + +> **sats**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:2765](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2765) diff --git a/modules/brk-client/docs/interfaces/ActivePricePattern.md b/modules/brk-client/docs/interfaces/ActivePricePattern.md new file mode 100644 index 000000000..e6b5ed456 --- /dev/null +++ b/modules/brk-client/docs/interfaces/ActivePricePattern.md @@ -0,0 +1,25 @@ +[**brk-client**](../README.md) + +*** + +[brk-client](../globals.md) / ActivePricePattern + +# Interface: ActivePricePattern + +Defined in: [Developer/brk/modules/brk-client/index.js:2706](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2706) + +## Properties + +### dollars + +> **dollars**: [`MetricPattern1`](../type-aliases/MetricPattern1.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:2707](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2707) + +*** + +### sats + +> **sats**: [`MetricPattern1`](../type-aliases/MetricPattern1.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:2708](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2708) diff --git a/modules/brk-client/docs/interfaces/AllPattern.md b/modules/brk-client/docs/interfaces/AllPattern.md new file mode 100644 index 000000000..31973c48d --- /dev/null +++ b/modules/brk-client/docs/interfaces/AllPattern.md @@ -0,0 +1,57 @@ +[**brk-client**](../README.md) + +*** + +[brk-client](../globals.md) / AllPattern + +# Interface: AllPattern + +Defined in: [Developer/brk/modules/brk-client/index.js:2463](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2463) + +## Properties + +### balanceDecreased + +> **balanceDecreased**: [`FullnessPattern`](FullnessPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:2464](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2464) + +*** + +### balanceIncreased + +> **balanceIncreased**: [`FullnessPattern`](FullnessPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:2465](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2465) + +*** + +### both + +> **both**: [`FullnessPattern`](FullnessPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:2466](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2466) + +*** + +### reactivated + +> **reactivated**: [`FullnessPattern`](FullnessPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:2467](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2467) + +*** + +### receiving + +> **receiving**: [`FullnessPattern`](FullnessPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:2468](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2468) + +*** + +### sending + +> **sending**: [`FullnessPattern`](FullnessPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:2469](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2469) diff --git a/modules/brk-client/docs/interfaces/ClassDaysInLossPattern.md b/modules/brk-client/docs/interfaces/ClassDaysInLossPattern.md new file mode 100644 index 000000000..0f5169c45 --- /dev/null +++ b/modules/brk-client/docs/interfaces/ClassDaysInLossPattern.md @@ -0,0 +1,111 @@ +[**brk-client**](../README.md) + +*** + +[brk-client](../globals.md) / ClassDaysInLossPattern + +# Interface: ClassDaysInLossPattern\ + +Defined in: [Developer/brk/modules/brk-client/index.js:1932](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1932) + +## Type Parameters + +### T + +`T` + +## Properties + +### \_2015 + +> **\_2015**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1933](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1933) + +*** + +### \_2016 + +> **\_2016**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1934](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1934) + +*** + +### \_2017 + +> **\_2017**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1935](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1935) + +*** + +### \_2018 + +> **\_2018**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1936](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1936) + +*** + +### \_2019 + +> **\_2019**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1937](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1937) + +*** + +### \_2020 + +> **\_2020**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1938](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1938) + +*** + +### \_2021 + +> **\_2021**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1939](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1939) + +*** + +### \_2022 + +> **\_2022**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1940](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1940) + +*** + +### \_2023 + +> **\_2023**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1941](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1941) + +*** + +### \_2024 + +> **\_2024**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1942](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1942) + +*** + +### \_2025 + +> **\_2025**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1943](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1943) + +*** + +### \_2026 + +> **\_2026**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1944](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1944) diff --git a/modules/brk-client/docs/interfaces/MetricsTree_Cointime_ReserveRisk.md b/modules/brk-client/docs/interfaces/MetricsTree_Cointime_ReserveRisk.md new file mode 100644 index 000000000..da2f64539 --- /dev/null +++ b/modules/brk-client/docs/interfaces/MetricsTree_Cointime_ReserveRisk.md @@ -0,0 +1,33 @@ +[**brk-client**](../README.md) + +*** + +[brk-client](../globals.md) / MetricsTree\_Cointime\_ReserveRisk + +# Interface: MetricsTree\_Cointime\_ReserveRisk + +Defined in: [Developer/brk/modules/brk-client/index.js:3093](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3093) + +## Properties + +### hodlBank + +> **hodlBank**: [`MetricPattern6`](../type-aliases/MetricPattern6.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3094](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3094) + +*** + +### reserveRisk + +> **reserveRisk**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3095](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3095) + +*** + +### vocdd365dSma + +> **vocdd365dSma**: [`MetricPattern6`](../type-aliases/MetricPattern6.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3096](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3096) diff --git a/modules/brk-client/docs/interfaces/MetricsTree_Distribution_AddressActivity.md b/modules/brk-client/docs/interfaces/MetricsTree_Distribution_AddressActivity.md new file mode 100644 index 000000000..c5918c39d --- /dev/null +++ b/modules/brk-client/docs/interfaces/MetricsTree_Distribution_AddressActivity.md @@ -0,0 +1,81 @@ +[**brk-client**](../README.md) + +*** + +[brk-client](../globals.md) / MetricsTree\_Distribution\_AddressActivity + +# Interface: MetricsTree\_Distribution\_AddressActivity + +Defined in: [Developer/brk/modules/brk-client/index.js:3153](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3153) + +## Properties + +### all + +> **all**: [`AllPattern`](AllPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3154](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3154) + +*** + +### p2a + +> **p2a**: [`AllPattern`](AllPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3155](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3155) + +*** + +### p2pk33 + +> **p2pk33**: [`AllPattern`](AllPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3156](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3156) + +*** + +### p2pk65 + +> **p2pk65**: [`AllPattern`](AllPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3157](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3157) + +*** + +### p2pkh + +> **p2pkh**: [`AllPattern`](AllPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3158](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3158) + +*** + +### p2sh + +> **p2sh**: [`AllPattern`](AllPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3159](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3159) + +*** + +### p2tr + +> **p2tr**: [`AllPattern`](AllPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3160](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3160) + +*** + +### p2wpkh + +> **p2wpkh**: [`AllPattern`](AllPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3161](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3161) + +*** + +### p2wsh + +> **p2wsh**: [`AllPattern`](AllPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3162](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3162) diff --git a/modules/brk-client/docs/interfaces/MetricsTree_Distribution_GrowthRate.md b/modules/brk-client/docs/interfaces/MetricsTree_Distribution_GrowthRate.md new file mode 100644 index 000000000..6646b23bf --- /dev/null +++ b/modules/brk-client/docs/interfaces/MetricsTree_Distribution_GrowthRate.md @@ -0,0 +1,81 @@ +[**brk-client**](../README.md) + +*** + +[brk-client](../globals.md) / MetricsTree\_Distribution\_GrowthRate + +# Interface: MetricsTree\_Distribution\_GrowthRate + +Defined in: [Developer/brk/modules/brk-client/index.js:3244](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3244) + +## Properties + +### all + +> **all**: [`FullnessPattern`](FullnessPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3245](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3245) + +*** + +### p2a + +> **p2a**: [`FullnessPattern`](FullnessPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3246](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3246) + +*** + +### p2pk33 + +> **p2pk33**: [`FullnessPattern`](FullnessPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3247](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3247) + +*** + +### p2pk65 + +> **p2pk65**: [`FullnessPattern`](FullnessPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3248](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3248) + +*** + +### p2pkh + +> **p2pkh**: [`FullnessPattern`](FullnessPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3249](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3249) + +*** + +### p2sh + +> **p2sh**: [`FullnessPattern`](FullnessPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3250](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3250) + +*** + +### p2tr + +> **p2tr**: [`FullnessPattern`](FullnessPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3251](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3251) + +*** + +### p2wpkh + +> **p2wpkh**: [`FullnessPattern`](FullnessPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3252](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3252) + +*** + +### p2wsh + +> **p2wsh**: [`FullnessPattern`](FullnessPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3253](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3253) diff --git a/modules/brk-client/docs/interfaces/MetricsTree_Distribution_NewAddrCount.md b/modules/brk-client/docs/interfaces/MetricsTree_Distribution_NewAddrCount.md new file mode 100644 index 000000000..d04c82827 --- /dev/null +++ b/modules/brk-client/docs/interfaces/MetricsTree_Distribution_NewAddrCount.md @@ -0,0 +1,81 @@ +[**brk-client**](../README.md) + +*** + +[brk-client](../globals.md) / MetricsTree\_Distribution\_NewAddrCount + +# Interface: MetricsTree\_Distribution\_NewAddrCount + +Defined in: [Developer/brk/modules/brk-client/index.js:3257](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3257) + +## Properties + +### all + +> **all**: [`DollarsPattern`](DollarsPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3258](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3258) + +*** + +### p2a + +> **p2a**: [`DollarsPattern`](DollarsPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3259](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3259) + +*** + +### p2pk33 + +> **p2pk33**: [`DollarsPattern`](DollarsPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3260](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3260) + +*** + +### p2pk65 + +> **p2pk65**: [`DollarsPattern`](DollarsPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3261](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3261) + +*** + +### p2pkh + +> **p2pkh**: [`DollarsPattern`](DollarsPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3262](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3262) + +*** + +### p2sh + +> **p2sh**: [`DollarsPattern`](DollarsPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3263](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3263) + +*** + +### p2tr + +> **p2tr**: [`DollarsPattern`](DollarsPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3264](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3264) + +*** + +### p2wpkh + +> **p2wpkh**: [`DollarsPattern`](DollarsPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3265](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3265) + +*** + +### p2wsh + +> **p2wsh**: [`DollarsPattern`](DollarsPattern.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3266](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3266) diff --git a/modules/brk-client/docs/interfaces/MetricsTree_Market_Dca_ClassAveragePrice.md b/modules/brk-client/docs/interfaces/MetricsTree_Market_Dca_ClassAveragePrice.md new file mode 100644 index 000000000..1389b35dc --- /dev/null +++ b/modules/brk-client/docs/interfaces/MetricsTree_Market_Dca_ClassAveragePrice.md @@ -0,0 +1,105 @@ +[**brk-client**](../README.md) + +*** + +[brk-client](../globals.md) / MetricsTree\_Market\_Dca\_ClassAveragePrice + +# Interface: MetricsTree\_Market\_Dca\_ClassAveragePrice + +Defined in: [Developer/brk/modules/brk-client/index.js:3768](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3768) + +## Properties + +### \_2015 + +> **\_2015**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3769](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3769) + +*** + +### \_2016 + +> **\_2016**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3770](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3770) + +*** + +### \_2017 + +> **\_2017**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3771](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3771) + +*** + +### \_2018 + +> **\_2018**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3772](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3772) + +*** + +### \_2019 + +> **\_2019**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3773](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3773) + +*** + +### \_2020 + +> **\_2020**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3774](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3774) + +*** + +### \_2021 + +> **\_2021**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3775](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3775) + +*** + +### \_2022 + +> **\_2022**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3776](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3776) + +*** + +### \_2023 + +> **\_2023**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3777](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3777) + +*** + +### \_2024 + +> **\_2024**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3778](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3778) + +*** + +### \_2025 + +> **\_2025**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3779](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3779) + +*** + +### \_2026 + +> **\_2026**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3780](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3780) diff --git a/modules/brk-client/docs/interfaces/MetricsTree_Market_Dca_ClassDaysInLoss.md b/modules/brk-client/docs/interfaces/MetricsTree_Market_Dca_ClassDaysInLoss.md new file mode 100644 index 000000000..e6796dd05 --- /dev/null +++ b/modules/brk-client/docs/interfaces/MetricsTree_Market_Dca_ClassDaysInLoss.md @@ -0,0 +1,105 @@ +[**brk-client**](../README.md) + +*** + +[brk-client](../globals.md) / MetricsTree\_Market\_Dca\_ClassDaysInLoss + +# Interface: MetricsTree\_Market\_Dca\_ClassDaysInLoss + +Defined in: [Developer/brk/modules/brk-client/index.js:3784](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3784) + +## Properties + +### \_2015 + +> **\_2015**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3785](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3785) + +*** + +### \_2016 + +> **\_2016**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3786](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3786) + +*** + +### \_2017 + +> **\_2017**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3787](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3787) + +*** + +### \_2018 + +> **\_2018**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3788](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3788) + +*** + +### \_2019 + +> **\_2019**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3789](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3789) + +*** + +### \_2020 + +> **\_2020**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3790](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3790) + +*** + +### \_2021 + +> **\_2021**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3791](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3791) + +*** + +### \_2022 + +> **\_2022**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3792](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3792) + +*** + +### \_2023 + +> **\_2023**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3793](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3793) + +*** + +### \_2024 + +> **\_2024**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3794](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3794) + +*** + +### \_2025 + +> **\_2025**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3795](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3795) + +*** + +### \_2026 + +> **\_2026**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3796](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3796) diff --git a/modules/brk-client/docs/interfaces/MetricsTree_Market_Dca_ClassMaxDrawdown.md b/modules/brk-client/docs/interfaces/MetricsTree_Market_Dca_ClassMaxDrawdown.md new file mode 100644 index 000000000..b768fe7bf --- /dev/null +++ b/modules/brk-client/docs/interfaces/MetricsTree_Market_Dca_ClassMaxDrawdown.md @@ -0,0 +1,105 @@ +[**brk-client**](../README.md) + +*** + +[brk-client](../globals.md) / MetricsTree\_Market\_Dca\_ClassMaxDrawdown + +# Interface: MetricsTree\_Market\_Dca\_ClassMaxDrawdown + +Defined in: [Developer/brk/modules/brk-client/index.js:3800](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3800) + +## Properties + +### \_2015 + +> **\_2015**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3801](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3801) + +*** + +### \_2016 + +> **\_2016**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3802](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3802) + +*** + +### \_2017 + +> **\_2017**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3803](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3803) + +*** + +### \_2018 + +> **\_2018**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3804](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3804) + +*** + +### \_2019 + +> **\_2019**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3805](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3805) + +*** + +### \_2020 + +> **\_2020**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3806](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3806) + +*** + +### \_2021 + +> **\_2021**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3807](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3807) + +*** + +### \_2022 + +> **\_2022**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3808](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3808) + +*** + +### \_2023 + +> **\_2023**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3809](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3809) + +*** + +### \_2024 + +> **\_2024**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3810](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3810) + +*** + +### \_2025 + +> **\_2025**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3811](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3811) + +*** + +### \_2026 + +> **\_2026**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3812](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3812) diff --git a/modules/brk-client/docs/interfaces/MetricsTree_Market_Dca_ClassMaxReturn.md b/modules/brk-client/docs/interfaces/MetricsTree_Market_Dca_ClassMaxReturn.md new file mode 100644 index 000000000..fe9653fb3 --- /dev/null +++ b/modules/brk-client/docs/interfaces/MetricsTree_Market_Dca_ClassMaxReturn.md @@ -0,0 +1,105 @@ +[**brk-client**](../README.md) + +*** + +[brk-client](../globals.md) / MetricsTree\_Market\_Dca\_ClassMaxReturn + +# Interface: MetricsTree\_Market\_Dca\_ClassMaxReturn + +Defined in: [Developer/brk/modules/brk-client/index.js:3816](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3816) + +## Properties + +### \_2015 + +> **\_2015**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3817](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3817) + +*** + +### \_2016 + +> **\_2016**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3818](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3818) + +*** + +### \_2017 + +> **\_2017**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3819](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3819) + +*** + +### \_2018 + +> **\_2018**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3820](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3820) + +*** + +### \_2019 + +> **\_2019**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3821](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3821) + +*** + +### \_2020 + +> **\_2020**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3822](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3822) + +*** + +### \_2021 + +> **\_2021**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3823](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3823) + +*** + +### \_2022 + +> **\_2022**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3824](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3824) + +*** + +### \_2023 + +> **\_2023**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3825](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3825) + +*** + +### \_2024 + +> **\_2024**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3826](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3826) + +*** + +### \_2025 + +> **\_2025**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3827](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3827) + +*** + +### \_2026 + +> **\_2026**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3828](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3828) diff --git a/modules/brk-client/docs/interfaces/MetricsTree_Market_Dca_ClassReturns.md b/modules/brk-client/docs/interfaces/MetricsTree_Market_Dca_ClassReturns.md new file mode 100644 index 000000000..9c3267eb8 --- /dev/null +++ b/modules/brk-client/docs/interfaces/MetricsTree_Market_Dca_ClassReturns.md @@ -0,0 +1,105 @@ +[**brk-client**](../README.md) + +*** + +[brk-client](../globals.md) / MetricsTree\_Market\_Dca\_ClassReturns + +# Interface: MetricsTree\_Market\_Dca\_ClassReturns + +Defined in: [Developer/brk/modules/brk-client/index.js:3832](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3832) + +## Properties + +### \_2015 + +> **\_2015**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3833](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3833) + +*** + +### \_2016 + +> **\_2016**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3834](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3834) + +*** + +### \_2017 + +> **\_2017**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3835](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3835) + +*** + +### \_2018 + +> **\_2018**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3836](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3836) + +*** + +### \_2019 + +> **\_2019**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3837](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3837) + +*** + +### \_2020 + +> **\_2020**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3838](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3838) + +*** + +### \_2021 + +> **\_2021**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3839](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3839) + +*** + +### \_2022 + +> **\_2022**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3840](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3840) + +*** + +### \_2023 + +> **\_2023**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3841](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3841) + +*** + +### \_2024 + +> **\_2024**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3842](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3842) + +*** + +### \_2025 + +> **\_2025**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3843](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3843) + +*** + +### \_2026 + +> **\_2026**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:3844](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3844) diff --git a/modules/brk-client/docs/interfaces/MetricsTree_Market_Dca_PeriodAveragePrice.md b/modules/brk-client/docs/interfaces/MetricsTree_Market_Dca_PeriodAveragePrice.md new file mode 100644 index 000000000..218d79f8f --- /dev/null +++ b/modules/brk-client/docs/interfaces/MetricsTree_Market_Dca_PeriodAveragePrice.md @@ -0,0 +1,105 @@ +[**brk-client**](../README.md) + +*** + +[brk-client](../globals.md) / MetricsTree\_Market\_Dca\_PeriodAveragePrice + +# Interface: MetricsTree\_Market\_Dca\_PeriodAveragePrice + +Defined in: [Developer/brk/modules/brk-client/index.js:3864](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3864) + +## Properties + +### \_10y + +> **\_10y**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3865](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3865) + +*** + +### \_1m + +> **\_1m**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3866](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3866) + +*** + +### \_1w + +> **\_1w**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3867](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3867) + +*** + +### \_1y + +> **\_1y**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3868](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3868) + +*** + +### \_2y + +> **\_2y**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3869](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3869) + +*** + +### \_3m + +> **\_3m**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3870](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3870) + +*** + +### \_3y + +> **\_3y**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3871](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3871) + +*** + +### \_4y + +> **\_4y**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3872](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3872) + +*** + +### \_5y + +> **\_5y**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3873](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3873) + +*** + +### \_6m + +> **\_6m**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3874](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3874) + +*** + +### \_6y + +> **\_6y**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3875](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3875) + +*** + +### \_8y + +> **\_8y**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3876](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3876) diff --git a/modules/brk-client/docs/interfaces/MetricsTree_Market_Lookback.md b/modules/brk-client/docs/interfaces/MetricsTree_Market_Lookback.md new file mode 100644 index 000000000..23231a174 --- /dev/null +++ b/modules/brk-client/docs/interfaces/MetricsTree_Market_Lookback.md @@ -0,0 +1,113 @@ +[**brk-client**](../README.md) + +*** + +[brk-client](../globals.md) / MetricsTree\_Market\_Lookback + +# Interface: MetricsTree\_Market\_Lookback + +Defined in: [Developer/brk/modules/brk-client/index.js:3903](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3903) + +## Properties + +### \_10y + +> **\_10y**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3904](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3904) + +*** + +### \_1d + +> **\_1d**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3905](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3905) + +*** + +### \_1m + +> **\_1m**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3906](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3906) + +*** + +### \_1w + +> **\_1w**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3907](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3907) + +*** + +### \_1y + +> **\_1y**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3908](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3908) + +*** + +### \_2y + +> **\_2y**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3909](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3909) + +*** + +### \_3m + +> **\_3m**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3910](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3910) + +*** + +### \_3y + +> **\_3y**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3911](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3911) + +*** + +### \_4y + +> **\_4y**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3912](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3912) + +*** + +### \_5y + +> **\_5y**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3913](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3913) + +*** + +### \_6m + +> **\_6m**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3914](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3914) + +*** + +### \_6y + +> **\_6y**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3915](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3915) + +*** + +### \_8y + +> **\_8y**: [`_0sdUsdPattern`](0sdUsdPattern.md) + +Defined in: [Developer/brk/modules/brk-client/index.js:3916](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3916) diff --git a/modules/brk-client/docs/interfaces/MetricsTree_Price_Usd.md b/modules/brk-client/docs/interfaces/MetricsTree_Price_Usd.md new file mode 100644 index 000000000..b6ee6d2bb --- /dev/null +++ b/modules/brk-client/docs/interfaces/MetricsTree_Price_Usd.md @@ -0,0 +1,25 @@ +[**brk-client**](../README.md) + +*** + +[brk-client](../globals.md) / MetricsTree\_Price\_Usd + +# Interface: MetricsTree\_Price\_Usd + +Defined in: [Developer/brk/modules/brk-client/index.js:4234](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L4234) + +## Properties + +### ohlc + +> **ohlc**: [`MetricPattern1`](../type-aliases/MetricPattern1.md)\<[`OHLCDollars`](OHLCDollars.md)\> + +Defined in: [Developer/brk/modules/brk-client/index.js:4235](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L4235) + +*** + +### split + +> **split**: [`SplitPattern2`](SplitPattern2.md)\<`number`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:4236](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L4236) diff --git a/modules/brk-client/docs/interfaces/PeriodDaysInLossPattern.md b/modules/brk-client/docs/interfaces/PeriodDaysInLossPattern.md new file mode 100644 index 000000000..3fb7693e9 --- /dev/null +++ b/modules/brk-client/docs/interfaces/PeriodDaysInLossPattern.md @@ -0,0 +1,111 @@ +[**brk-client**](../README.md) + +*** + +[brk-client](../globals.md) / PeriodDaysInLossPattern + +# Interface: PeriodDaysInLossPattern\ + +Defined in: [Developer/brk/modules/brk-client/index.js:1891](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1891) + +## Type Parameters + +### T + +`T` + +## Properties + +### \_10y + +> **\_10y**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1892](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1892) + +*** + +### \_1m + +> **\_1m**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1893](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1893) + +*** + +### \_1w + +> **\_1w**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1894](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1894) + +*** + +### \_1y + +> **\_1y**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1895](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1895) + +*** + +### \_2y + +> **\_2y**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1896](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1896) + +*** + +### \_3m + +> **\_3m**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1897](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1897) + +*** + +### \_3y + +> **\_3y**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1898](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1898) + +*** + +### \_4y + +> **\_4y**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1899](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1899) + +*** + +### \_5y + +> **\_5y**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1900](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1900) + +*** + +### \_6m + +> **\_6m**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1901](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1901) + +*** + +### \_6y + +> **\_6y**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1902](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1902) + +*** + +### \_8y + +> **\_8y**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\> + +Defined in: [Developer/brk/modules/brk-client/index.js:1903](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1903) diff --git a/modules/brk-client/docs/type-aliases/SatsFract.md b/modules/brk-client/docs/type-aliases/SatsFract.md new file mode 100644 index 000000000..982860074 --- /dev/null +++ b/modules/brk-client/docs/type-aliases/SatsFract.md @@ -0,0 +1,13 @@ +[**brk-client**](../README.md) + +*** + +[brk-client](../globals.md) / SatsFract + +# Type Alias: SatsFract + +> **SatsFract**\<\> = `number` + +Defined in: [Developer/brk/modules/brk-client/index.js:607](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L607) + +## Type Parameters diff --git a/modules/brk-client/docs/type-aliases/StoredI8.md b/modules/brk-client/docs/type-aliases/StoredI8.md new file mode 100644 index 000000000..fd46c2e3f --- /dev/null +++ b/modules/brk-client/docs/type-aliases/StoredI8.md @@ -0,0 +1,13 @@ +[**brk-client**](../README.md) + +*** + +[brk-client](../globals.md) / StoredI8 + +# Type Alias: StoredI8 + +> **StoredI8**\<\> = `number` + +Defined in: [Developer/brk/modules/brk-client/index.js:625](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L625) + +## Type Parameters diff --git a/modules/brk-client/index.js b/modules/brk-client/index.js index ff74ffd5f..507e46f4b 100644 --- a/modules/brk-client/index.js +++ b/modules/brk-client/index.js @@ -838,15 +838,120 @@ class BrkError extends Error { } } +// Date conversion constants and helpers +const _GENESIS = new Date(2009, 0, 3); // dateindex 0, weekindex 0 +const _DAY_ONE = new Date(2009, 0, 9); // dateindex 1 (6 day gap after genesis) +const _MS_PER_DAY = 24 * 60 * 60 * 1000; +const _MS_PER_WEEK = 7 * _MS_PER_DAY; +const _DATE_INDEXES = new Set(['dateindex', 'weekindex', 'monthindex', 'yearindex', 'quarterindex', 'semesterindex', 'decadeindex']); + +/** @param {number} months @returns {globalThis.Date} */ +const _addMonths = (months) => new Date(2009, months, 1); + +/** + * Convert an index value to a Date for date-based indexes. + * @param {Index} index - The index type + * @param {number} i - The index value + * @returns {globalThis.Date} + */ +function indexToDate(index, i) { + switch (index) { + case 'dateindex': return i === 0 ? _GENESIS : new Date(_DAY_ONE.getTime() + (i - 1) * _MS_PER_DAY); + case 'weekindex': return new Date(_GENESIS.getTime() + i * _MS_PER_WEEK); + case 'monthindex': return _addMonths(i); + case 'yearindex': return new Date(2009 + i, 0, 1); + case 'quarterindex': return _addMonths(i * 3); + case 'semesterindex': return _addMonths(i * 6); + case 'decadeindex': return new Date(2009 + i * 10, 0, 1); + default: throw new Error(`${index} is not a date-based index`); + } +} + +/** + * Check if an index type is date-based. + * @param {Index} index + * @returns {boolean} + */ +function isDateIndex(index) { + return _DATE_INDEXES.has(index); +} + +/** + * Wrap raw metric data with helper methods. + * @template T + * @param {MetricData} raw - Raw JSON response + * @returns {MetricData} + */ +function _wrapMetricData(raw) { + const { index, start, end, data } = raw; + return /** @type {MetricData} */ ({ + ...raw, + dates() { + /** @type {globalThis.Date[]} */ + const result = []; + for (let i = start; i < end; i++) result.push(indexToDate(index, i)); + return result; + }, + indexes() { + /** @type {number[]} */ + const result = []; + for (let i = start; i < end; i++) result.push(i); + return result; + }, + toDateMap() { + /** @type {Map} */ + const map = new Map(); + for (let i = 0; i < data.length; i++) map.set(indexToDate(index, start + i), data[i]); + return map; + }, + toIndexMap() { + /** @type {Map} */ + const map = new Map(); + for (let i = 0; i < data.length; i++) map.set(start + i, data[i]); + return map; + }, + dateEntries() { + /** @type {Array<[globalThis.Date, T]>} */ + const result = []; + for (let i = 0; i < data.length; i++) result.push([indexToDate(index, start + i), data[i]]); + return result; + }, + indexEntries() { + /** @type {Array<[number, T]>} */ + const result = []; + for (let i = 0; i < data.length; i++) result.push([start + i, data[i]]); + return result; + }, + *iter() { + for (let i = 0; i < data.length; i++) yield [start + i, data[i]]; + }, + *iterDates() { + for (let i = 0; i < data.length; i++) yield [indexToDate(index, start + i), data[i]]; + }, + [Symbol.iterator]() { + return this.iter(); + }, + }); +} + /** * @template T * @typedef {Object} MetricData * @property {number} version - Version of the metric data + * @property {Index} index - The index type used for this query * @property {number} total - Total number of data points * @property {number} start - Start index (inclusive) * @property {number} end - End index (exclusive) * @property {string} stamp - ISO 8601 timestamp of when the response was generated * @property {T[]} data - The metric data + * @property {() => globalThis.Date[]} dates - Convert index range to dates (date-based indexes only) + * @property {() => number[]} indexes - Get index range as array + * @property {() => Map} toDateMap - Return data as Map keyed by date (date-based only) + * @property {() => Map} toIndexMap - Return data as Map keyed by index + * @property {() => Array<[globalThis.Date, T]>} dateEntries - Return data as [date, value] pairs (date-based only) + * @property {() => Array<[number, T]>} indexEntries - Return data as [index, value] pairs + * @property {() => IterableIterator<[number, T]>} iter - Iterate over [index, value] pairs + * @property {() => IterableIterator<[globalThis.Date, T]>} iterDates - Iterate over [date, value] pairs (date-based only) */ /** @typedef {MetricData} AnyMetricData */ @@ -940,18 +1045,18 @@ function _endpoint(client, name, index) { * @returns {RangeBuilder} */ const rangeBuilder = (start, end) => ({ - fetch(onUpdate) { return client.getJson(buildPath(start, end), onUpdate); }, + fetch(onUpdate) { return client._fetchMetricData(buildPath(start, end), onUpdate); }, fetchCsv() { return client.getText(buildPath(start, end, 'csv')); }, then(resolve, reject) { return this.fetch().then(resolve, reject); }, }); /** - * @param {number} index + * @param {number} idx * @returns {SingleItemBuilder} */ - const singleItemBuilder = (index) => ({ - fetch(onUpdate) { return client.getJson(buildPath(index, index + 1), onUpdate); }, - fetchCsv() { return client.getText(buildPath(index, index + 1, 'csv')); }, + const singleItemBuilder = (idx) => ({ + fetch(onUpdate) { return client._fetchMetricData(buildPath(idx, idx + 1), onUpdate); }, + fetchCsv() { return client.getText(buildPath(idx, idx + 1, 'csv')); }, then(resolve, reject) { return this.fetch().then(resolve, reject); }, }); @@ -961,19 +1066,19 @@ function _endpoint(client, name, index) { */ const skippedBuilder = (start) => ({ take(n) { return rangeBuilder(start, start + n); }, - fetch(onUpdate) { return client.getJson(buildPath(start, undefined), onUpdate); }, + fetch(onUpdate) { return client._fetchMetricData(buildPath(start, undefined), onUpdate); }, fetchCsv() { return client.getText(buildPath(start, undefined, 'csv')); }, then(resolve, reject) { return this.fetch().then(resolve, reject); }, }); /** @type {MetricEndpointBuilder} */ const endpoint = { - get(index) { return singleItemBuilder(index); }, + get(idx) { return singleItemBuilder(idx); }, slice(start, end) { return rangeBuilder(start, end); }, first(n) { return rangeBuilder(undefined, n); }, last(n) { return n === 0 ? rangeBuilder(undefined, 0) : rangeBuilder(-n, undefined); }, skip(n) { return skippedBuilder(n); }, - fetch(onUpdate) { return client.getJson(buildPath(), onUpdate); }, + fetch(onUpdate) { return client._fetchMetricData(buildPath(), onUpdate); }, fetchCsv() { return client.getText(buildPath(undefined, undefined, 'csv')); }, then(resolve, reject) { return this.fetch().then(resolve, reject); }, get path() { return p; }, @@ -1053,6 +1158,19 @@ class BrkClientBase { const res = await this.get(path); return res.text(); } + + /** + * Fetch metric data and wrap with helper methods (internal) + * @template T + * @param {string} path + * @param {(value: MetricData) => void} [onUpdate] + * @returns {Promise>} + */ + async _fetchMetricData(path, onUpdate) { + const wrappedOnUpdate = onUpdate ? (/** @type {MetricData} */ raw) => onUpdate(_wrapMetricData(raw)) : undefined; + const raw = await this.getJson(path, wrappedOnUpdate); + return _wrapMetricData(raw); + } } /** @@ -5265,6 +5383,25 @@ class BrkClient extends BrkClientBase { } }); + /** + * Convert an index value to a Date for date-based indexes. + * @param {Index} index - The index type + * @param {number} i - The index value + * @returns {globalThis.Date} + */ + indexToDate(index, i) { + return indexToDate(index, i); + } + + /** + * Check if an index type is date-based. + * @param {Index} index + * @returns {boolean} + */ + isDateIndex(index) { + return isDateIndex(index); + } + /** * @param {BrkClientOptions|string} options */ diff --git a/modules/brk-client/tests/metric_data.js b/modules/brk-client/tests/metric_data.js new file mode 100644 index 000000000..5aa4b5f05 --- /dev/null +++ b/modules/brk-client/tests/metric_data.js @@ -0,0 +1,221 @@ +/** + * Tests for MetricData helper methods and date conversion functions. + * Run: node tests/metric_data.js + */ + +import { BrkClient } from "../index.js"; + +const client = new BrkClient("http://localhost:3110"); + +console.log("Testing MetricData helpers...\n"); + +// Fetch a date-based metric +console.log("1. Fetching price data (dateindex):"); +const price = await client.metrics.price.usd.split.close.by.dateindex.first(5); +console.log( + ` Total: ${price.total}, Start: ${price.start}, End: ${price.end}`, +); + +// Test indexes() +console.log("\n2. indexes():"); +const indexes = price.indexes(); +console.log(` ${JSON.stringify(indexes)}`); +if (indexes.length !== 5) throw new Error("Expected 5 indexes"); +if (indexes[0] !== price.start) + throw new Error("First index should equal start"); + +// Test dates() +console.log("\n3. dates():"); +const dates = price.dates(); +console.log( + ` First: ${dates[0].toISOString()}, Last: ${dates[dates.length - 1].toISOString()}`, +); +if (dates.length !== 5) throw new Error("Expected 5 dates"); +// DateIndex 0 = Jan 3, 2009 (genesis) +if ( + dates[0].getFullYear() !== 2009 || + dates[0].getMonth() !== 0 || + dates[0].getDate() !== 3 +) { + throw new Error( + `Expected genesis date (2009-01-03), got ${dates[0].toISOString()}`, + ); +} + +// Test toIndexMap() +console.log("\n4. toIndexMap():"); +const indexMap = price.toIndexMap(); +console.log( + ` Size: ${indexMap.size}, First value: ${indexMap.get(price.start)}`, +); +if (indexMap.size !== 5) throw new Error("Expected map size 5"); +if (indexMap.get(price.start) !== price.data[0]) + throw new Error("First value mismatch"); + +// Test toDateMap() +console.log("\n5. toDateMap():"); +const dateMap = price.toDateMap(); +console.log(` Size: ${dateMap.size}`); +if (dateMap.size !== 5) throw new Error("Expected map size 5"); + +// Test indexEntries() +console.log("\n6. indexEntries():"); +const indexEntries = price.indexEntries(); +console.log(` First: [${indexEntries[0][0]}, ${indexEntries[0][1]}]`); +if (indexEntries[0][0] !== price.start) + throw new Error("First entry index mismatch"); +if (indexEntries[0][1] !== price.data[0]) + throw new Error("First entry value mismatch"); + +// Test dateEntries() +console.log("\n7. dateEntries():"); +const dateEntries = price.dateEntries(); +console.log( + ` First: [${dateEntries[0][0].toISOString()}, ${dateEntries[0][1]}]`, +); +if (dateEntries[0][1] !== price.data[0]) + throw new Error("First entry value mismatch"); + +// Test iter() +console.log("\n8. iter():"); +const iterResult = [...price.iter()]; +console.log( + ` Length: ${iterResult.length}, First: [${iterResult[0][0]}, ${iterResult[0][1]}]`, +); +if (iterResult.length !== 5) throw new Error("Expected 5 items"); + +// Test iterDates() +console.log("\n9. iterDates():"); +const iterDatesResult = [...price.iterDates()]; +console.log( + ` Length: ${iterDatesResult.length}, First date: ${iterDatesResult[0][0].toISOString()}`, +); +if (iterDatesResult.length !== 5) throw new Error("Expected 5 items"); + +// Test Symbol.iterator (for...of) +console.log("\n10. for...of iteration:"); +let count = 0; +for (const [idx, val] of price.iter()) { + count++; +} +console.log(` Iterated ${count} items`); +if (count !== 5) throw new Error("Expected 5 iterations"); + +// Test with non-date-based index (height) +console.log("\n11. Testing height-based metric:"); +const heightMetric = + await client.metrics.price.usd.split.close.by.height.last(3); +console.log( + ` Total: ${heightMetric.total}, Start: ${heightMetric.start}, End: ${heightMetric.end}`, +); +const heightIndexes = heightMetric.indexes(); +console.log(` Indexes: ${JSON.stringify(heightIndexes)}`); +if (heightIndexes[0] !== heightMetric.start) + throw new Error("Height index mismatch"); + +// Test different date indexes +console.log("\n12. Testing monthindex:"); +const monthMetric = + await client.metrics.price.usd.split.close.by.monthindex.first(3); +const monthDates = monthMetric.dates(); +console.log(` First month: ${monthDates[0].toISOString()}`); +// MonthIndex 0 = Jan 1, 2009 +if ( + monthDates[0].getFullYear() !== 2009 || + monthDates[0].getMonth() !== 0 || + monthDates[0].getDate() !== 1 +) { + throw new Error(`Expected 2009-01-01, got ${monthDates[0].toISOString()}`); +} + +// Test indexToDate directly +console.log("\n13. Testing indexToDate():"); +const genesis = client.indexToDate("dateindex", 0); +if ( + genesis.getFullYear() !== 2009 || + genesis.getMonth() !== 0 || + genesis.getDate() !== 3 +) { + throw new Error(`Expected genesis 2009-01-03, got ${genesis.toISOString()}`); +} +const dayOne = client.indexToDate("dateindex", 1); +if ( + dayOne.getFullYear() !== 2009 || + dayOne.getMonth() !== 0 || + dayOne.getDate() !== 9 +) { + throw new Error(`Expected day one 2009-01-09, got ${dayOne.toISOString()}`); +} +console.log(` dateindex 0: ${genesis.toISOString()}`); +console.log(` dateindex 1: ${dayOne.toISOString()}`); + +// Test weekindex +const week0 = client.indexToDate("weekindex", 0); +const week1 = client.indexToDate("weekindex", 1); +if (week0.getTime() !== genesis.getTime()) + throw new Error("weekindex 0 should equal genesis"); +console.log(` weekindex 0: ${week0.toISOString()}`); +console.log(` weekindex 1: ${week1.toISOString()}`); + +// Test yearindex +const year0 = client.indexToDate("yearindex", 0); +const year1 = client.indexToDate("yearindex", 1); +if ( + year0.getFullYear() !== 2009 || + year0.getMonth() !== 0 || + year0.getDate() !== 1 +) { + throw new Error(`Expected 2009-01-01, got ${year0.toISOString()}`); +} +if (year1.getFullYear() !== 2010) throw new Error("yearindex 1 should be 2010"); +console.log(` yearindex 0: ${year0.toISOString()}`); +console.log(` yearindex 1: ${year1.toISOString()}`); + +// Test quarterindex +const q0 = client.indexToDate("quarterindex", 0); +const q1 = client.indexToDate("quarterindex", 1); +if (q0.getMonth() !== 0) throw new Error("Q0 should be January"); +if (q1.getMonth() !== 3) throw new Error("Q1 should be April"); +console.log(` quarterindex 0: ${q0.toISOString()}`); +console.log(` quarterindex 1: ${q1.toISOString()}`); + +// Test semesterindex +const s0 = client.indexToDate("semesterindex", 0); +const s1 = client.indexToDate("semesterindex", 1); +if (s0.getMonth() !== 0) throw new Error("S0 should be January"); +if (s1.getMonth() !== 6) throw new Error("S1 should be July"); +console.log(` semesterindex 0: ${s0.toISOString()}`); +console.log(` semesterindex 1: ${s1.toISOString()}`); + +// Test decadeindex +const d0 = client.indexToDate("decadeindex", 0); +const d1 = client.indexToDate("decadeindex", 1); +if (d0.getFullYear() !== 2009) throw new Error("decadeindex 0 should be 2009"); +if (d1.getFullYear() !== 2019) throw new Error("decadeindex 1 should be 2019"); +console.log(` decadeindex 0: ${d0.toISOString()}`); +console.log(` decadeindex 1: ${d1.toISOString()}`); + +// Test isDateIndex +console.log("\n14. Testing isDateIndex():"); +const dateIndexes = /** @type {const} */ ([ + "dateindex", + "weekindex", + "monthindex", + "yearindex", + "quarterindex", + "semesterindex", + "decadeindex", +]); +const nonDateIndexes = /** @type {const} */ (["height", "txindex"]); +for (const idx of dateIndexes) { + if (!client.isDateIndex(idx)) + throw new Error(`${idx} should be a date index`); +} +for (const idx of nonDateIndexes) { + if (client.isDateIndex(idx)) + throw new Error(`${idx} should not be a date index`); +} +console.log(` Date indexes: ${dateIndexes.join(", ")} ✓`); +console.log(` Non-date indexes: ${nonDateIndexes.join(", ")} ✓`); + +console.log("\nAll MetricData tests passed!"); diff --git a/packages/brk_client/brk_client/__init__.py b/packages/brk_client/brk_client/__init__.py index bbbdcf2cf..c99c44998 100644 --- a/packages/brk_client/brk_client/__init__.py +++ b/packages/brk_client/brk_client/__init__.py @@ -3,11 +3,16 @@ from __future__ import annotations from dataclasses import dataclass -from typing import TypeVar, Generic, Any, Optional, List, Literal, TypedDict, Union, Protocol, overload +from typing import TypeVar, Generic, Any, Optional, List, Literal, TypedDict, Union, Protocol, overload, Iterator, Tuple, TYPE_CHECKING from http.client import HTTPSConnection, HTTPConnection from urllib.parse import urlparse +from datetime import date, timedelta import json +if TYPE_CHECKING: + import pandas as pd # type: ignore[import-not-found] + import polars as pl # type: ignore[import-not-found] + T = TypeVar('T') # Type definitions @@ -1043,16 +1048,114 @@ def _p(prefix: str, acc: str) -> str: return f"{prefix}_{acc}" if acc else prefix +# Date conversion constants +_GENESIS = date(2009, 1, 3) # dateindex 0, weekindex 0 +_DAY_ONE = date(2009, 1, 9) # dateindex 1 (6 day gap after genesis) +_DATE_INDEXES = frozenset(['dateindex', 'weekindex', 'monthindex', 'yearindex', 'quarterindex', 'semesterindex', 'decadeindex']) + +def is_date_index(index: str) -> bool: + """Check if an index type is date-based.""" + return index in _DATE_INDEXES + +def index_to_date(index: str, i: int) -> date: + """Convert an index value to a date for date-based indexes.""" + if index == 'dateindex': + return _GENESIS if i == 0 else _DAY_ONE + timedelta(days=i - 1) + elif index == 'weekindex': + return _GENESIS + timedelta(weeks=i) + elif index == 'monthindex': + return date(2009 + i // 12, i % 12 + 1, 1) + elif index == 'yearindex': + return date(2009 + i, 1, 1) + elif index == 'quarterindex': + m = i * 3 + return date(2009 + m // 12, m % 12 + 1, 1) + elif index == 'semesterindex': + m = i * 6 + return date(2009 + m // 12, m % 12 + 1, 1) + elif index == 'decadeindex': + return date(2009 + i * 10, 1, 1) + else: + raise ValueError(f"{index} is not a date-based index") + + @dataclass class MetricData(Generic[T]): """Metric data with range information.""" version: int + index: Index total: int start: int end: int stamp: str data: List[T] + def dates(self) -> List[date]: + """Convert index range to dates. Only works for date-based indexes.""" + return [index_to_date(self.index, i) for i in range(self.start, self.end)] + + def indexes(self) -> List[int]: + """Get index range as list.""" + return list(range(self.start, self.end)) + + def to_date_dict(self) -> dict[date, T]: + """Return data as {date: value} dict. Only works for date-based indexes.""" + return dict(zip(self.dates(), self.data)) + + def to_index_dict(self) -> dict[int, T]: + """Return data as {index: value} dict.""" + return dict(zip(range(self.start, self.end), self.data)) + + def date_items(self) -> List[Tuple[date, T]]: + """Return data as [(date, value), ...] pairs. Only works for date-based indexes.""" + return list(zip(self.dates(), self.data)) + + def index_items(self) -> List[Tuple[int, T]]: + """Return data as [(index, value), ...] pairs.""" + return list(zip(range(self.start, self.end), self.data)) + + def iter(self) -> Iterator[Tuple[int, T]]: + """Iterate over (index, value) pairs.""" + return iter(zip(range(self.start, self.end), self.data)) + + def iter_dates(self) -> Iterator[Tuple[date, T]]: + """Iterate over (date, value) pairs. Date-based indexes only.""" + return iter(zip(self.dates(), self.data)) + + def __iter__(self) -> Iterator[Tuple[int, T]]: + """Default iteration over (index, value) pairs.""" + return self.iter() + + def to_polars(self, with_dates: bool = True) -> pl.DataFrame: + """Convert to Polars DataFrame. Requires polars to be installed. + + Returns a DataFrame with columns: + - 'date' (date) and 'value' (T) if with_dates=True and index is date-based + - 'index' (int) and 'value' (T) otherwise + """ + try: + import polars as pl # type: ignore[import-not-found] + except ImportError: + raise ImportError("polars is required: pip install polars") + if with_dates and self.index in _DATE_INDEXES: + return pl.DataFrame({"date": self.dates(), "value": self.data}) + return pl.DataFrame({"index": list(range(self.start, self.end)), "value": self.data}) + + def to_pandas(self, with_dates: bool = True) -> pd.DataFrame: + """Convert to Pandas DataFrame. Requires pandas to be installed. + + Returns a DataFrame with columns: + - 'date' (date) and 'value' (T) if with_dates=True and index is date-based + - 'index' (int) and 'value' (T) otherwise + """ + try: + import pandas as pd # type: ignore[import-not-found] + except ImportError: + raise ImportError("pandas is required: pip install pandas") + if with_dates and self.index in _DATE_INDEXES: + return pd.DataFrame({"date": self.dates(), "value": self.data}) + return pd.DataFrame({"index": list(range(self.start, self.end)), "value": self.data}) + # Type alias for non-generic usage AnyMetricData = MetricData[Any] @@ -1089,9 +1192,8 @@ class _EndpointConfig: p = self.path() return f"{p}?{query}" if query else p - def get_json(self) -> MetricData: - data = self.client.get_json(self._build_path()) - return MetricData(**data) + def get_metric(self) -> MetricData: + return MetricData(**self.client.get_json(self._build_path())) def get_csv(self) -> str: return self.client.get_text(self._build_path(format='csv')) @@ -1105,7 +1207,7 @@ class RangeBuilder(Generic[T]): def fetch(self) -> MetricData[T]: """Fetch the range as parsed JSON.""" - return self._config.get_json() + return self._config.get_metric() def fetch_csv(self) -> str: """Fetch the range as CSV string.""" @@ -1120,7 +1222,7 @@ class SingleItemBuilder(Generic[T]): def fetch(self) -> MetricData[T]: """Fetch the single item.""" - return self._config.get_json() + return self._config.get_metric() def fetch_csv(self) -> str: """Fetch as CSV.""" @@ -1143,7 +1245,7 @@ class SkippedBuilder(Generic[T]): def fetch(self) -> MetricData[T]: """Fetch from skipped position to end.""" - return self._config.get_json() + return self._config.get_metric() def fetch_csv(self) -> str: """Fetch as CSV.""" @@ -1227,7 +1329,7 @@ class MetricEndpointBuilder(Generic[T]): def fetch(self) -> MetricData[T]: """Fetch all data as parsed JSON.""" - return self._config.get_json() + return self._config.get_metric() def fetch_csv(self) -> str: """Fetch all data as CSV string.""" @@ -4958,6 +5060,14 @@ class BrkClient(BrkClientBase): """ return MetricEndpointBuilder(self, metric, index) + def index_to_date(self, index: Index, i: int) -> date: + """Convert an index value to a date for date-based indexes.""" + return index_to_date(index, i) + + def is_date_index(self, index: Index) -> bool: + """Check if an index type is date-based.""" + return is_date_index(index) + def get_api(self) -> Any: """Compact OpenAPI specification. diff --git a/packages/brk_client/pyproject.toml b/packages/brk_client/pyproject.toml index 218d17c82..365a33d8c 100644 --- a/packages/brk_client/pyproject.toml +++ b/packages/brk_client/pyproject.toml @@ -25,6 +25,8 @@ Repository = "https://github.com/bitcoinresearchkit/brk" [dependency-groups] dev = [ + "pandas>=2.3.3", + "polars>=1.36.1", "pydoc-markdown>=4.8.2", "pytest", ] diff --git a/packages/brk_client/tests/test_metric_data.py b/packages/brk_client/tests/test_metric_data.py new file mode 100644 index 000000000..e68207525 --- /dev/null +++ b/packages/brk_client/tests/test_metric_data.py @@ -0,0 +1,340 @@ +# Tests for MetricData helper methods including polars/pandas conversion +# Run: uv run pytest tests/test_metric_data.py -v + +from datetime import date +import pytest + +from brk_client import MetricData, index_to_date, is_date_index + + +# Test data fixtures +@pytest.fixture +def date_based_metric(): + """MetricData with dateindex (date-based).""" + return MetricData( + version=1, + index="dateindex", + total=100, + start=0, + end=5, + stamp="2024-01-01T00:00:00Z", + data=[100, 200, 300, 400, 500], + ) + + +@pytest.fixture +def height_based_metric(): + """MetricData with height (non-date-based).""" + return MetricData( + version=1, + index="height", + total=1000, + start=800000, + end=800005, + stamp="2024-01-01T00:00:00Z", + data=[1.5, 2.5, 3.5, 4.5, 5.5], + ) + + +@pytest.fixture +def month_based_metric(): + """MetricData with monthindex.""" + return MetricData( + version=1, + index="monthindex", + total=200, + start=0, + end=3, + stamp="2024-01-01T00:00:00Z", + data=[1000, 2000, 3000], + ) + + +# ============ Date conversion tests ============ + +class TestIndexToDate: + """Test the index_to_date function.""" + + def test_dateindex_zero(self): + """DateIndex 0 is genesis: Jan 3, 2009.""" + assert index_to_date("dateindex", 0) == date(2009, 1, 3) + + def test_dateindex_one(self): + """DateIndex 1 is Jan 9, 2009 (6 day gap after genesis).""" + assert index_to_date("dateindex", 1) == date(2009, 1, 9) + + def test_dateindex_two(self): + """DateIndex 2 is Jan 10, 2009.""" + assert index_to_date("dateindex", 2) == date(2009, 1, 10) + + def test_weekindex_zero(self): + """WeekIndex 0 is genesis: Jan 3, 2009.""" + assert index_to_date("weekindex", 0) == date(2009, 1, 3) + + def test_weekindex_one(self): + """WeekIndex 1 is Jan 10, 2009 (one week after genesis).""" + assert index_to_date("weekindex", 1) == date(2009, 1, 10) + + def test_monthindex_zero(self): + """MonthIndex 0 is Jan 1, 2009.""" + assert index_to_date("monthindex", 0) == date(2009, 1, 1) + + def test_monthindex_one(self): + """MonthIndex 1 is Feb 1, 2009.""" + assert index_to_date("monthindex", 1) == date(2009, 2, 1) + + def test_monthindex_twelve(self): + """MonthIndex 12 is Jan 1, 2010.""" + assert index_to_date("monthindex", 12) == date(2010, 1, 1) + + def test_yearindex_zero(self): + """YearIndex 0 is Jan 1, 2009.""" + assert index_to_date("yearindex", 0) == date(2009, 1, 1) + + def test_yearindex_one(self): + """YearIndex 1 is Jan 1, 2010.""" + assert index_to_date("yearindex", 1) == date(2010, 1, 1) + + def test_quarterindex_zero(self): + """QuarterIndex 0 is Q1 2009: Jan 1, 2009.""" + assert index_to_date("quarterindex", 0) == date(2009, 1, 1) + + def test_quarterindex_one(self): + """QuarterIndex 1 is Q2 2009: Apr 1, 2009.""" + assert index_to_date("quarterindex", 1) == date(2009, 4, 1) + + def test_quarterindex_four(self): + """QuarterIndex 4 is Q1 2010: Jan 1, 2010.""" + assert index_to_date("quarterindex", 4) == date(2010, 1, 1) + + def test_semesterindex_zero(self): + """SemesterIndex 0 is H1 2009: Jan 1, 2009.""" + assert index_to_date("semesterindex", 0) == date(2009, 1, 1) + + def test_semesterindex_one(self): + """SemesterIndex 1 is H2 2009: Jul 1, 2009.""" + assert index_to_date("semesterindex", 1) == date(2009, 7, 1) + + def test_semesterindex_two(self): + """SemesterIndex 2 is H1 2010: Jan 1, 2010.""" + assert index_to_date("semesterindex", 2) == date(2010, 1, 1) + + def test_decadeindex_zero(self): + """DecadeIndex 0 is 2009: Jan 1, 2009.""" + assert index_to_date("decadeindex", 0) == date(2009, 1, 1) + + def test_decadeindex_one(self): + """DecadeIndex 1 is 2019: Jan 1, 2019.""" + assert index_to_date("decadeindex", 1) == date(2019, 1, 1) + + def test_invalid_index_raises(self): + """Non-date-based index raises ValueError.""" + with pytest.raises(ValueError): + index_to_date("height", 0) + + +class TestIsDateIndex: + """Test the is_date_index function.""" + + def test_dateindex_is_date_based(self): + assert is_date_index("dateindex") is True + + def test_weekindex_is_date_based(self): + assert is_date_index("weekindex") is True + + def test_monthindex_is_date_based(self): + assert is_date_index("monthindex") is True + + def test_yearindex_is_date_based(self): + assert is_date_index("yearindex") is True + + def test_quarterindex_is_date_based(self): + assert is_date_index("quarterindex") is True + + def test_semesterindex_is_date_based(self): + assert is_date_index("semesterindex") is True + + def test_decadeindex_is_date_based(self): + assert is_date_index("decadeindex") is True + + def test_height_is_not_date_based(self): + assert is_date_index("height") is False + + def test_txindex_is_not_date_based(self): + assert is_date_index("txindex") is False + + +# ============ MetricData helper method tests ============ + +class TestMetricDataHelpers: + """Test MetricData helper methods.""" + + def test_indexes_returns_range(self, date_based_metric): + """indexes() returns list of index values.""" + assert date_based_metric.indexes() == [0, 1, 2, 3, 4] + + def test_indexes_with_offset(self, height_based_metric): + """indexes() respects start/end offsets.""" + assert height_based_metric.indexes() == [800000, 800001, 800002, 800003, 800004] + + def test_dates_for_dateindex(self, date_based_metric): + """dates() returns correct dates for dateindex.""" + dates = date_based_metric.dates() + assert dates[0] == date(2009, 1, 3) # dateindex 0 = genesis + assert dates[1] == date(2009, 1, 9) # dateindex 1 = day one + assert dates[2] == date(2009, 1, 10) # dateindex 2 + + def test_dates_for_monthindex(self, month_based_metric): + """dates() returns correct dates for monthindex.""" + dates = month_based_metric.dates() + assert dates[0] == date(2009, 1, 1) + assert dates[1] == date(2009, 2, 1) + assert dates[2] == date(2009, 3, 1) + + def test_to_index_dict(self, date_based_metric): + """to_index_dict() returns {index: value} mapping.""" + d = date_based_metric.to_index_dict() + assert d[0] == 100 + assert d[1] == 200 + assert d[4] == 500 + + def test_to_date_dict(self, date_based_metric): + """to_date_dict() returns {date: value} mapping.""" + d = date_based_metric.to_date_dict() + assert d[date(2009, 1, 3)] == 100 # genesis + assert d[date(2009, 1, 9)] == 200 # day one + + def test_index_items(self, date_based_metric): + """index_items() returns [(index, value), ...] pairs.""" + items = date_based_metric.index_items() + assert items[0] == (0, 100) + assert items[4] == (4, 500) + + def test_date_items(self, date_based_metric): + """date_items() returns [(date, value), ...] pairs.""" + items = date_based_metric.date_items() + assert items[0] == (date(2009, 1, 3), 100) + assert items[1] == (date(2009, 1, 9), 200) + + def test_iter(self, date_based_metric): + """iter() yields (index, value) pairs.""" + result = list(date_based_metric.iter()) + assert result == [(0, 100), (1, 200), (2, 300), (3, 400), (4, 500)] + + def test_iter_dates(self, date_based_metric): + """iter_dates() yields (date, value) pairs.""" + result = list(date_based_metric.iter_dates()) + assert result[0] == (date(2009, 1, 3), 100) + assert result[1] == (date(2009, 1, 9), 200) + + def test_default_iteration(self, date_based_metric): + """Default iteration uses iter().""" + result = list(date_based_metric) + assert result == [(0, 100), (1, 200), (2, 300), (3, 400), (4, 500)] + + +# ============ Polars tests ============ + +class TestPolarsConversion: + """Test MetricData.to_polars() conversion.""" + + @pytest.fixture(autouse=True) + def check_polars(self): + """Skip if polars not installed.""" + pytest.importorskip("polars") + + def test_to_polars_with_dates(self, date_based_metric): + """to_polars() includes date column for date-based index.""" + import polars as pl + + df = date_based_metric.to_polars() + assert isinstance(df, pl.DataFrame) + assert "date" in df.columns + assert "value" in df.columns + assert len(df) == 5 + assert df["value"].to_list() == [100, 200, 300, 400, 500] + + def test_to_polars_without_dates(self, date_based_metric): + """to_polars(with_dates=False) uses index column.""" + import polars as pl + + df = date_based_metric.to_polars(with_dates=False) + assert "index" in df.columns + assert "date" not in df.columns + assert df["index"].to_list() == [0, 1, 2, 3, 4] + + def test_to_polars_non_date_index(self, height_based_metric): + """to_polars() uses index column for non-date-based index.""" + import polars as pl + + df = height_based_metric.to_polars() + assert "index" in df.columns + assert "date" not in df.columns + assert df["index"].to_list() == [800000, 800001, 800002, 800003, 800004] + assert df["value"].to_list() == [1.5, 2.5, 3.5, 4.5, 5.5] + + def test_to_polars_monthindex(self, month_based_metric): + """to_polars() works with monthindex.""" + import polars as pl + + df = month_based_metric.to_polars() + assert "date" in df.columns + assert len(df) == 3 + # Polars stores dates as date type + dates = df["date"].to_list() + assert dates[0] == date(2009, 1, 1) + assert dates[1] == date(2009, 2, 1) + assert dates[2] == date(2009, 3, 1) + + +# ============ Pandas tests ============ + +class TestPandasConversion: + """Test MetricData.to_pandas() conversion.""" + + @pytest.fixture(autouse=True) + def check_pandas(self): + """Skip if pandas not installed.""" + pytest.importorskip("pandas") + + def test_to_pandas_with_dates(self, date_based_metric): + """to_pandas() includes date column for date-based index.""" + import pandas as pd + + df = date_based_metric.to_pandas() + assert isinstance(df, pd.DataFrame) + assert "date" in df.columns + assert "value" in df.columns + assert len(df) == 5 + assert df["value"].tolist() == [100, 200, 300, 400, 500] + + def test_to_pandas_without_dates(self, date_based_metric): + """to_pandas(with_dates=False) uses index column.""" + import pandas as pd + + df = date_based_metric.to_pandas(with_dates=False) + assert "index" in df.columns + assert "date" not in df.columns + assert df["index"].tolist() == [0, 1, 2, 3, 4] + + def test_to_pandas_non_date_index(self, height_based_metric): + """to_pandas() uses index column for non-date-based index.""" + import pandas as pd + + df = height_based_metric.to_pandas() + assert "index" in df.columns + assert "date" not in df.columns + assert df["index"].tolist() == [800000, 800001, 800002, 800003, 800004] + assert df["value"].tolist() == [1.5, 2.5, 3.5, 4.5, 5.5] + + def test_to_pandas_monthindex(self, month_based_metric): + """to_pandas() works with monthindex.""" + import pandas as pd + + df = month_based_metric.to_pandas() + assert "date" in df.columns + assert len(df) == 3 + dates = df["date"].tolist() + assert dates[0] == date(2009, 1, 1) + assert dates[1] == date(2009, 2, 1) + assert dates[2] == date(2009, 3, 1) diff --git a/packages/brk_client/uv.lock b/packages/brk_client/uv.lock index ff4e7ef8a..5effa62de 100644 --- a/packages/brk_client/uv.lock +++ b/packages/brk_client/uv.lock @@ -2,7 +2,13 @@ version = 1 revision = 3 requires-python = ">=3.9" resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", "python_full_version < '3.10'", ] @@ -44,11 +50,15 @@ wheels = [ [[package]] name = "brk-client" -version = "0.1.0b1" +version = "0.1.1" source = { editable = "." } [package.dev-dependencies] dev = [ + { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "pandas", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "polars", version = "1.36.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "polars", version = "1.37.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "pydoc-markdown" }, { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, @@ -58,6 +68,8 @@ dev = [ [package.metadata.requires-dev] dev = [ + { name = "pandas", specifier = ">=2.3.3" }, + { name = "polars", specifier = ">=1.36.1" }, { name = "pydoc-markdown", specifier = ">=4.8.2" }, { name = "pytest" }, ] @@ -196,7 +208,13 @@ name = "click" version = "8.3.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", ] dependencies = [ { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, @@ -307,7 +325,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -340,7 +358,13 @@ name = "iniconfig" version = "2.3.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } wheels = [ @@ -495,6 +519,213 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ba/58/eab08df9dbd69d9e21fc5e7be6f67454f386336ec71e6b64e378a2dddea4/nr.util-0.8.12-py3-none-any.whl", hash = "sha256:91da02ac9795eb8e015372275c1efe54bac9051231ee9b0e7e6f96b0b4e7d2bb", size = 90319, upload-time = "2022-06-20T13:29:27.312Z" }, ] +[[package]] +name = "numpy" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/75/10dd1f8116a8b796cb2c737b674e02d02e80454bda953fa7e65d8c12b016/numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78", size = 18902015, upload-time = "2024-08-26T20:19:40.945Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/91/3495b3237510f79f5d81f2508f9f13fea78ebfdf07538fc7444badda173d/numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece", size = 21165245, upload-time = "2024-08-26T20:04:14.625Z" }, + { url = "https://files.pythonhosted.org/packages/05/33/26178c7d437a87082d11019292dce6d3fe6f0e9026b7b2309cbf3e489b1d/numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04", size = 13738540, upload-time = "2024-08-26T20:04:36.784Z" }, + { url = "https://files.pythonhosted.org/packages/ec/31/cc46e13bf07644efc7a4bf68df2df5fb2a1a88d0cd0da9ddc84dc0033e51/numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66", size = 5300623, upload-time = "2024-08-26T20:04:46.491Z" }, + { url = "https://files.pythonhosted.org/packages/6e/16/7bfcebf27bb4f9d7ec67332ffebee4d1bf085c84246552d52dbb548600e7/numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b", size = 6901774, upload-time = "2024-08-26T20:04:58.173Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a3/561c531c0e8bf082c5bef509d00d56f82e0ea7e1e3e3a7fc8fa78742a6e5/numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd", size = 13907081, upload-time = "2024-08-26T20:05:19.098Z" }, + { url = "https://files.pythonhosted.org/packages/fa/66/f7177ab331876200ac7563a580140643d1179c8b4b6a6b0fc9838de2a9b8/numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318", size = 19523451, upload-time = "2024-08-26T20:05:47.479Z" }, + { url = "https://files.pythonhosted.org/packages/25/7f/0b209498009ad6453e4efc2c65bcdf0ae08a182b2b7877d7ab38a92dc542/numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8", size = 19927572, upload-time = "2024-08-26T20:06:17.137Z" }, + { url = "https://files.pythonhosted.org/packages/3e/df/2619393b1e1b565cd2d4c4403bdd979621e2c4dea1f8532754b2598ed63b/numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326", size = 14400722, upload-time = "2024-08-26T20:06:39.16Z" }, + { url = "https://files.pythonhosted.org/packages/22/ad/77e921b9f256d5da36424ffb711ae79ca3f451ff8489eeca544d0701d74a/numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97", size = 6472170, upload-time = "2024-08-26T20:06:50.361Z" }, + { url = "https://files.pythonhosted.org/packages/10/05/3442317535028bc29cf0c0dd4c191a4481e8376e9f0db6bcf29703cadae6/numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131", size = 15905558, upload-time = "2024-08-26T20:07:13.881Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cf/034500fb83041aa0286e0fb16e7c76e5c8b67c0711bb6e9e9737a717d5fe/numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448", size = 21169137, upload-time = "2024-08-26T20:07:45.345Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d9/32de45561811a4b87fbdee23b5797394e3d1504b4a7cf40c10199848893e/numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195", size = 13703552, upload-time = "2024-08-26T20:08:06.666Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ca/2f384720020c7b244d22508cb7ab23d95f179fcfff33c31a6eeba8d6c512/numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57", size = 5298957, upload-time = "2024-08-26T20:08:15.83Z" }, + { url = "https://files.pythonhosted.org/packages/0e/78/a3e4f9fb6aa4e6fdca0c5428e8ba039408514388cf62d89651aade838269/numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a", size = 6905573, upload-time = "2024-08-26T20:08:27.185Z" }, + { url = "https://files.pythonhosted.org/packages/a0/72/cfc3a1beb2caf4efc9d0b38a15fe34025230da27e1c08cc2eb9bfb1c7231/numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669", size = 13914330, upload-time = "2024-08-26T20:08:48.058Z" }, + { url = "https://files.pythonhosted.org/packages/ba/a8/c17acf65a931ce551fee11b72e8de63bf7e8a6f0e21add4c937c83563538/numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951", size = 19534895, upload-time = "2024-08-26T20:09:16.536Z" }, + { url = "https://files.pythonhosted.org/packages/ba/86/8767f3d54f6ae0165749f84648da9dcc8cd78ab65d415494962c86fac80f/numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9", size = 19937253, upload-time = "2024-08-26T20:09:46.263Z" }, + { url = "https://files.pythonhosted.org/packages/df/87/f76450e6e1c14e5bb1eae6836478b1028e096fd02e85c1c37674606ab752/numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15", size = 14414074, upload-time = "2024-08-26T20:10:08.483Z" }, + { url = "https://files.pythonhosted.org/packages/5c/ca/0f0f328e1e59f73754f06e1adfb909de43726d4f24c6a3f8805f34f2b0fa/numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4", size = 6470640, upload-time = "2024-08-26T20:10:19.732Z" }, + { url = "https://files.pythonhosted.org/packages/eb/57/3a3f14d3a759dcf9bf6e9eda905794726b758819df4663f217d658a58695/numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc", size = 15910230, upload-time = "2024-08-26T20:10:43.413Z" }, + { url = "https://files.pythonhosted.org/packages/45/40/2e117be60ec50d98fa08c2f8c48e09b3edea93cfcabd5a9ff6925d54b1c2/numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b", size = 20895803, upload-time = "2024-08-26T20:11:13.916Z" }, + { url = "https://files.pythonhosted.org/packages/46/92/1b8b8dee833f53cef3e0a3f69b2374467789e0bb7399689582314df02651/numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e", size = 13471835, upload-time = "2024-08-26T20:11:34.779Z" }, + { url = "https://files.pythonhosted.org/packages/7f/19/e2793bde475f1edaea6945be141aef6c8b4c669b90c90a300a8954d08f0a/numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c", size = 5038499, upload-time = "2024-08-26T20:11:43.902Z" }, + { url = "https://files.pythonhosted.org/packages/e3/ff/ddf6dac2ff0dd50a7327bcdba45cb0264d0e96bb44d33324853f781a8f3c/numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c", size = 6633497, upload-time = "2024-08-26T20:11:55.09Z" }, + { url = "https://files.pythonhosted.org/packages/72/21/67f36eac8e2d2cd652a2e69595a54128297cdcb1ff3931cfc87838874bd4/numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692", size = 13621158, upload-time = "2024-08-26T20:12:14.95Z" }, + { url = "https://files.pythonhosted.org/packages/39/68/e9f1126d757653496dbc096cb429014347a36b228f5a991dae2c6b6cfd40/numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a", size = 19236173, upload-time = "2024-08-26T20:12:44.049Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e9/1f5333281e4ebf483ba1c888b1d61ba7e78d7e910fdd8e6499667041cc35/numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c", size = 19634174, upload-time = "2024-08-26T20:13:13.634Z" }, + { url = "https://files.pythonhosted.org/packages/71/af/a469674070c8d8408384e3012e064299f7a2de540738a8e414dcfd639996/numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded", size = 14099701, upload-time = "2024-08-26T20:13:34.851Z" }, + { url = "https://files.pythonhosted.org/packages/d0/3d/08ea9f239d0e0e939b6ca52ad403c84a2bce1bde301a8eb4888c1c1543f1/numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5", size = 6174313, upload-time = "2024-08-26T20:13:45.653Z" }, + { url = "https://files.pythonhosted.org/packages/b2/b5/4ac39baebf1fdb2e72585c8352c56d063b6126be9fc95bd2bb5ef5770c20/numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a", size = 15606179, upload-time = "2024-08-26T20:14:08.786Z" }, + { url = "https://files.pythonhosted.org/packages/43/c1/41c8f6df3162b0c6ffd4437d729115704bd43363de0090c7f913cfbc2d89/numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c", size = 21169942, upload-time = "2024-08-26T20:14:40.108Z" }, + { url = "https://files.pythonhosted.org/packages/39/bc/fd298f308dcd232b56a4031fd6ddf11c43f9917fbc937e53762f7b5a3bb1/numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd", size = 13711512, upload-time = "2024-08-26T20:15:00.985Z" }, + { url = "https://files.pythonhosted.org/packages/96/ff/06d1aa3eeb1c614eda245c1ba4fb88c483bee6520d361641331872ac4b82/numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b", size = 5306976, upload-time = "2024-08-26T20:15:10.876Z" }, + { url = "https://files.pythonhosted.org/packages/2d/98/121996dcfb10a6087a05e54453e28e58694a7db62c5a5a29cee14c6e047b/numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729", size = 6906494, upload-time = "2024-08-26T20:15:22.055Z" }, + { url = "https://files.pythonhosted.org/packages/15/31/9dffc70da6b9bbf7968f6551967fc21156207366272c2a40b4ed6008dc9b/numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1", size = 13912596, upload-time = "2024-08-26T20:15:42.452Z" }, + { url = "https://files.pythonhosted.org/packages/b9/14/78635daab4b07c0930c919d451b8bf8c164774e6a3413aed04a6d95758ce/numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd", size = 19526099, upload-time = "2024-08-26T20:16:11.048Z" }, + { url = "https://files.pythonhosted.org/packages/26/4c/0eeca4614003077f68bfe7aac8b7496f04221865b3a5e7cb230c9d055afd/numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d", size = 19932823, upload-time = "2024-08-26T20:16:40.171Z" }, + { url = "https://files.pythonhosted.org/packages/f1/46/ea25b98b13dccaebddf1a803f8c748680d972e00507cd9bc6dcdb5aa2ac1/numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d", size = 14404424, upload-time = "2024-08-26T20:17:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/c8/a6/177dd88d95ecf07e722d21008b1b40e681a929eb9e329684d449c36586b2/numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa", size = 6476809, upload-time = "2024-08-26T20:17:13.553Z" }, + { url = "https://files.pythonhosted.org/packages/ea/2b/7fc9f4e7ae5b507c1a3a21f0f15ed03e794c1242ea8a242ac158beb56034/numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73", size = 15911314, upload-time = "2024-08-26T20:17:36.72Z" }, + { url = "https://files.pythonhosted.org/packages/8f/3b/df5a870ac6a3be3a86856ce195ef42eec7ae50d2a202be1f5a4b3b340e14/numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8", size = 21025288, upload-time = "2024-08-26T20:18:07.732Z" }, + { url = "https://files.pythonhosted.org/packages/2c/97/51af92f18d6f6f2d9ad8b482a99fb74e142d71372da5d834b3a2747a446e/numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4", size = 6762793, upload-time = "2024-08-26T20:18:19.125Z" }, + { url = "https://files.pythonhosted.org/packages/12/46/de1fbd0c1b5ccaa7f9a005b66761533e2f6a3e560096682683a223631fe9/numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c", size = 19334885, upload-time = "2024-08-26T20:18:47.237Z" }, + { url = "https://files.pythonhosted.org/packages/cc/dc/d330a6faefd92b446ec0f0dfea4c3207bb1fef3c4771d19cf4543efd2c78/numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385", size = 15828784, upload-time = "2024-08-26T20:19:11.19Z" }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, + { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, + { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, + { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, + { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, + { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, + { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, + { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, + { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" }, + { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" }, + { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, + { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, + { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, + { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, + { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, + { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, + { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, + { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, + { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, + { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, + { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, + { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, + { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, + { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, + { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, + { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, + { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, + { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, + { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, + { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, + { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, + { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, + { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, + { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/24/62/ae72ff66c0f1fd959925b4c11f8c2dea61f47f6acaea75a08512cdfe3fed/numpy-2.4.1.tar.gz", hash = "sha256:a1ceafc5042451a858231588a104093474c6a5c57dcc724841f5c888d237d690", size = 20721320, upload-time = "2026-01-10T06:44:59.619Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/34/2b1bc18424f3ad9af577f6ce23600319968a70575bd7db31ce66731bbef9/numpy-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0cce2a669e3c8ba02ee563c7835f92c153cf02edff1ae05e1823f1dde21b16a5", size = 16944563, upload-time = "2026-01-10T06:42:14.615Z" }, + { url = "https://files.pythonhosted.org/packages/2c/57/26e5f97d075aef3794045a6ca9eada6a4ed70eb9a40e7a4a93f9ac80d704/numpy-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:899d2c18024984814ac7e83f8f49d8e8180e2fbe1b2e252f2e7f1d06bea92425", size = 12645658, upload-time = "2026-01-10T06:42:17.298Z" }, + { url = "https://files.pythonhosted.org/packages/8e/ba/80fc0b1e3cb2fd5c6143f00f42eb67762aa043eaa05ca924ecc3222a7849/numpy-2.4.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:09aa8a87e45b55a1c2c205d42e2808849ece5c484b2aab11fecabec3841cafba", size = 5474132, upload-time = "2026-01-10T06:42:19.637Z" }, + { url = "https://files.pythonhosted.org/packages/40/ae/0a5b9a397f0e865ec171187c78d9b57e5588afc439a04ba9cab1ebb2c945/numpy-2.4.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:edee228f76ee2dab4579fad6f51f6a305de09d444280109e0f75df247ff21501", size = 6804159, upload-time = "2026-01-10T06:42:21.44Z" }, + { url = "https://files.pythonhosted.org/packages/86/9c/841c15e691c7085caa6fd162f063eff494099c8327aeccd509d1ab1e36ab/numpy-2.4.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a92f227dbcdc9e4c3e193add1a189a9909947d4f8504c576f4a732fd0b54240a", size = 14708058, upload-time = "2026-01-10T06:42:23.546Z" }, + { url = "https://files.pythonhosted.org/packages/5d/9d/7862db06743f489e6a502a3b93136d73aea27d97b2cf91504f70a27501d6/numpy-2.4.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:538bf4ec353709c765ff75ae616c34d3c3dca1a68312727e8f2676ea644f8509", size = 16651501, upload-time = "2026-01-10T06:42:25.909Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9c/6fc34ebcbd4015c6e5f0c0ce38264010ce8a546cb6beacb457b84a75dfc8/numpy-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ac08c63cb7779b85e9d5318e6c3518b424bc1f364ac4cb2c6136f12e5ff2dccc", size = 16492627, upload-time = "2026-01-10T06:42:28.938Z" }, + { url = "https://files.pythonhosted.org/packages/aa/63/2494a8597502dacda439f61b3c0db4da59928150e62be0e99395c3ad23c5/numpy-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f9c360ecef085e5841c539a9a12b883dff005fbd7ce46722f5e9cef52634d82", size = 18585052, upload-time = "2026-01-10T06:42:31.312Z" }, + { url = "https://files.pythonhosted.org/packages/6a/93/098e1162ae7522fc9b618d6272b77404c4656c72432ecee3abc029aa3de0/numpy-2.4.1-cp311-cp311-win32.whl", hash = "sha256:0f118ce6b972080ba0758c6087c3617b5ba243d806268623dc34216d69099ba0", size = 6236575, upload-time = "2026-01-10T06:42:33.872Z" }, + { url = "https://files.pythonhosted.org/packages/8c/de/f5e79650d23d9e12f38a7bc6b03ea0835b9575494f8ec94c11c6e773b1b1/numpy-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:18e14c4d09d55eef39a6ab5b08406e84bc6869c1e34eef45564804f90b7e0574", size = 12604479, upload-time = "2026-01-10T06:42:35.778Z" }, + { url = "https://files.pythonhosted.org/packages/dd/65/e1097a7047cff12ce3369bd003811516b20ba1078dbdec135e1cd7c16c56/numpy-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:6461de5113088b399d655d45c3897fa188766415d0f568f175ab071c8873bd73", size = 10578325, upload-time = "2026-01-10T06:42:38.518Z" }, + { url = "https://files.pythonhosted.org/packages/78/7f/ec53e32bf10c813604edf07a3682616bd931d026fcde7b6d13195dfb684a/numpy-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d3703409aac693fa82c0aee023a1ae06a6e9d065dba10f5e8e80f642f1e9d0a2", size = 16656888, upload-time = "2026-01-10T06:42:40.913Z" }, + { url = "https://files.pythonhosted.org/packages/b8/e0/1f9585d7dae8f14864e948fd7fa86c6cb72dee2676ca2748e63b1c5acfe0/numpy-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7211b95ca365519d3596a1d8688a95874cc94219d417504d9ecb2df99fa7bfa8", size = 12373956, upload-time = "2026-01-10T06:42:43.091Z" }, + { url = "https://files.pythonhosted.org/packages/8e/43/9762e88909ff2326f5e7536fa8cb3c49fb03a7d92705f23e6e7f553d9cb3/numpy-2.4.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:5adf01965456a664fc727ed69cc71848f28d063217c63e1a0e200a118d5eec9a", size = 5202567, upload-time = "2026-01-10T06:42:45.107Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ee/34b7930eb61e79feb4478800a4b95b46566969d837546aa7c034c742ef98/numpy-2.4.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:26f0bcd9c79a00e339565b303badc74d3ea2bd6d52191eeca5f95936cad107d0", size = 6549459, upload-time = "2026-01-10T06:42:48.152Z" }, + { url = "https://files.pythonhosted.org/packages/79/e3/5f115fae982565771be994867c89bcd8d7208dbfe9469185497d70de5ddf/numpy-2.4.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0093e85df2960d7e4049664b26afc58b03236e967fb942354deef3208857a04c", size = 14404859, upload-time = "2026-01-10T06:42:49.947Z" }, + { url = "https://files.pythonhosted.org/packages/d9/7d/9c8a781c88933725445a859cac5d01b5871588a15969ee6aeb618ba99eee/numpy-2.4.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ad270f438cbdd402c364980317fb6b117d9ec5e226fff5b4148dd9aa9fc6e02", size = 16371419, upload-time = "2026-01-10T06:42:52.409Z" }, + { url = "https://files.pythonhosted.org/packages/a6/d2/8aa084818554543f17cf4162c42f162acbd3bb42688aefdba6628a859f77/numpy-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:297c72b1b98100c2e8f873d5d35fb551fce7040ade83d67dd51d38c8d42a2162", size = 16182131, upload-time = "2026-01-10T06:42:54.694Z" }, + { url = "https://files.pythonhosted.org/packages/60/db/0425216684297c58a8df35f3284ef56ec4a043e6d283f8a59c53562caf1b/numpy-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cf6470d91d34bf669f61d515499859fa7a4c2f7c36434afb70e82df7217933f9", size = 18295342, upload-time = "2026-01-10T06:42:56.991Z" }, + { url = "https://files.pythonhosted.org/packages/31/4c/14cb9d86240bd8c386c881bafbe43f001284b7cce3bc01623ac9475da163/numpy-2.4.1-cp312-cp312-win32.whl", hash = "sha256:b6bcf39112e956594b3331316d90c90c90fb961e39696bda97b89462f5f3943f", size = 5959015, upload-time = "2026-01-10T06:42:59.631Z" }, + { url = "https://files.pythonhosted.org/packages/51/cf/52a703dbeb0c65807540d29699fef5fda073434ff61846a564d5c296420f/numpy-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:e1a27bb1b2dee45a2a53f5ca6ff2d1a7f135287883a1689e930d44d1ff296c87", size = 12310730, upload-time = "2026-01-10T06:43:01.627Z" }, + { url = "https://files.pythonhosted.org/packages/69/80/a828b2d0ade5e74a9fe0f4e0a17c30fdc26232ad2bc8c9f8b3197cf7cf18/numpy-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:0e6e8f9d9ecf95399982019c01223dc130542960a12edfa8edd1122dfa66a8a8", size = 10312166, upload-time = "2026-01-10T06:43:03.673Z" }, + { url = "https://files.pythonhosted.org/packages/04/68/732d4b7811c00775f3bd522a21e8dd5a23f77eb11acdeb663e4a4ebf0ef4/numpy-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d797454e37570cfd61143b73b8debd623c3c0952959adb817dd310a483d58a1b", size = 16652495, upload-time = "2026-01-10T06:43:06.283Z" }, + { url = "https://files.pythonhosted.org/packages/20/ca/857722353421a27f1465652b2c66813eeeccea9d76d5f7b74b99f298e60e/numpy-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82c55962006156aeef1629b953fd359064aa47e4d82cfc8e67f0918f7da3344f", size = 12368657, upload-time = "2026-01-10T06:43:09.094Z" }, + { url = "https://files.pythonhosted.org/packages/81/0d/2377c917513449cc6240031a79d30eb9a163d32a91e79e0da47c43f2c0c8/numpy-2.4.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:71abbea030f2cfc3092a0ff9f8c8fdefdc5e0bf7d9d9c99663538bb0ecdac0b9", size = 5197256, upload-time = "2026-01-10T06:43:13.634Z" }, + { url = "https://files.pythonhosted.org/packages/17/39/569452228de3f5de9064ac75137082c6214be1f5c532016549a7923ab4b5/numpy-2.4.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5b55aa56165b17aaf15520beb9cbd33c9039810e0d9643dd4379e44294c7303e", size = 6545212, upload-time = "2026-01-10T06:43:15.661Z" }, + { url = "https://files.pythonhosted.org/packages/8c/a4/77333f4d1e4dac4395385482557aeecf4826e6ff517e32ca48e1dafbe42a/numpy-2.4.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0faba4a331195bfa96f93dd9dfaa10b2c7aa8cda3a02b7fd635e588fe821bf5", size = 14402871, upload-time = "2026-01-10T06:43:17.324Z" }, + { url = "https://files.pythonhosted.org/packages/ba/87/d341e519956273b39d8d47969dd1eaa1af740615394fe67d06f1efa68773/numpy-2.4.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e3087f53e2b4428766b54932644d148613c5a595150533ae7f00dab2f319a8", size = 16359305, upload-time = "2026-01-10T06:43:19.376Z" }, + { url = "https://files.pythonhosted.org/packages/32/91/789132c6666288eaa20ae8066bb99eba1939362e8f1a534949a215246e97/numpy-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:49e792ec351315e16da54b543db06ca8a86985ab682602d90c60ef4ff4db2a9c", size = 16181909, upload-time = "2026-01-10T06:43:21.808Z" }, + { url = "https://files.pythonhosted.org/packages/cf/b8/090b8bd27b82a844bb22ff8fdf7935cb1980b48d6e439ae116f53cdc2143/numpy-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79e9e06c4c2379db47f3f6fc7a8652e7498251789bf8ff5bd43bf478ef314ca2", size = 18284380, upload-time = "2026-01-10T06:43:23.957Z" }, + { url = "https://files.pythonhosted.org/packages/67/78/722b62bd31842ff029412271556a1a27a98f45359dea78b1548a3a9996aa/numpy-2.4.1-cp313-cp313-win32.whl", hash = "sha256:3d1a100e48cb266090a031397863ff8a30050ceefd798f686ff92c67a486753d", size = 5957089, upload-time = "2026-01-10T06:43:27.535Z" }, + { url = "https://files.pythonhosted.org/packages/da/a6/cf32198b0b6e18d4fbfa9a21a992a7fca535b9bb2b0cdd217d4a3445b5ca/numpy-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:92a0e65272fd60bfa0d9278e0484c2f52fe03b97aedc02b357f33fe752c52ffb", size = 12307230, upload-time = "2026-01-10T06:43:29.298Z" }, + { url = "https://files.pythonhosted.org/packages/44/6c/534d692bfb7d0afe30611320c5fb713659dcb5104d7cc182aff2aea092f5/numpy-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:20d4649c773f66cc2fc36f663e091f57c3b7655f936a4c681b4250855d1da8f5", size = 10313125, upload-time = "2026-01-10T06:43:31.782Z" }, + { url = "https://files.pythonhosted.org/packages/da/a1/354583ac5c4caa566de6ddfbc42744409b515039e085fab6e0ff942e0df5/numpy-2.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f93bc6892fe7b0663e5ffa83b61aab510aacffd58c16e012bb9352d489d90cb7", size = 12496156, upload-time = "2026-01-10T06:43:34.237Z" }, + { url = "https://files.pythonhosted.org/packages/51/b0/42807c6e8cce58c00127b1dc24d365305189991f2a7917aa694a109c8d7d/numpy-2.4.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:178de8f87948163d98a4c9ab5bee4ce6519ca918926ec8df195af582de28544d", size = 5324663, upload-time = "2026-01-10T06:43:36.211Z" }, + { url = "https://files.pythonhosted.org/packages/fe/55/7a621694010d92375ed82f312b2f28017694ed784775269115323e37f5e2/numpy-2.4.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:98b35775e03ab7f868908b524fc0a84d38932d8daf7b7e1c3c3a1b6c7a2c9f15", size = 6645224, upload-time = "2026-01-10T06:43:37.884Z" }, + { url = "https://files.pythonhosted.org/packages/50/96/9fa8635ed9d7c847d87e30c834f7109fac5e88549d79ef3324ab5c20919f/numpy-2.4.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:941c2a93313d030f219f3a71fd3d91a728b82979a5e8034eb2e60d394a2b83f9", size = 14462352, upload-time = "2026-01-10T06:43:39.479Z" }, + { url = "https://files.pythonhosted.org/packages/03/d1/8cf62d8bb2062da4fb82dd5d49e47c923f9c0738032f054e0a75342faba7/numpy-2.4.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:529050522e983e00a6c1c6b67411083630de8b57f65e853d7b03d9281b8694d2", size = 16407279, upload-time = "2026-01-10T06:43:41.93Z" }, + { url = "https://files.pythonhosted.org/packages/86/1c/95c86e17c6b0b31ce6ef219da00f71113b220bcb14938c8d9a05cee0ff53/numpy-2.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2302dc0224c1cbc49bb94f7064f3f923a971bfae45c33870dcbff63a2a550505", size = 16248316, upload-time = "2026-01-10T06:43:44.121Z" }, + { url = "https://files.pythonhosted.org/packages/30/b4/e7f5ff8697274c9d0fa82398b6a372a27e5cef069b37df6355ccb1f1db1a/numpy-2.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9171a42fcad32dcf3fa86f0a4faa5e9f8facefdb276f54b8b390d90447cff4e2", size = 18329884, upload-time = "2026-01-10T06:43:46.613Z" }, + { url = "https://files.pythonhosted.org/packages/37/a4/b073f3e9d77f9aec8debe8ca7f9f6a09e888ad1ba7488f0c3b36a94c03ac/numpy-2.4.1-cp313-cp313t-win32.whl", hash = "sha256:382ad67d99ef49024f11d1ce5dcb5ad8432446e4246a4b014418ba3a1175a1f4", size = 6081138, upload-time = "2026-01-10T06:43:48.854Z" }, + { url = "https://files.pythonhosted.org/packages/16/16/af42337b53844e67752a092481ab869c0523bc95c4e5c98e4dac4e9581ac/numpy-2.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:62fea415f83ad8fdb6c20840578e5fbaf5ddd65e0ec6c3c47eda0f69da172510", size = 12447478, upload-time = "2026-01-10T06:43:50.476Z" }, + { url = "https://files.pythonhosted.org/packages/6c/f8/fa85b2eac68ec631d0b631abc448552cb17d39afd17ec53dcbcc3537681a/numpy-2.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a7870e8c5fc11aef57d6fea4b4085e537a3a60ad2cdd14322ed531fdca68d261", size = 10382981, upload-time = "2026-01-10T06:43:52.575Z" }, + { url = "https://files.pythonhosted.org/packages/1b/a7/ef08d25698e0e4b4efbad8d55251d20fe2a15f6d9aa7c9b30cd03c165e6f/numpy-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3869ea1ee1a1edc16c29bbe3a2f2a4e515cc3a44d43903ad41e0cacdbaf733dc", size = 16652046, upload-time = "2026-01-10T06:43:54.797Z" }, + { url = "https://files.pythonhosted.org/packages/8f/39/e378b3e3ca13477e5ac70293ec027c438d1927f18637e396fe90b1addd72/numpy-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e867df947d427cdd7a60e3e271729090b0f0df80f5f10ab7dd436f40811699c3", size = 12378858, upload-time = "2026-01-10T06:43:57.099Z" }, + { url = "https://files.pythonhosted.org/packages/c3/74/7ec6154f0006910ed1fdbb7591cf4432307033102b8a22041599935f8969/numpy-2.4.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:e3bd2cb07841166420d2fa7146c96ce00cb3410664cbc1a6be028e456c4ee220", size = 5207417, upload-time = "2026-01-10T06:43:59.037Z" }, + { url = "https://files.pythonhosted.org/packages/f7/b7/053ac11820d84e42f8feea5cb81cc4fcd1091499b45b1ed8c7415b1bf831/numpy-2.4.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:f0a90aba7d521e6954670550e561a4cb925713bd944445dbe9e729b71f6cabee", size = 6542643, upload-time = "2026-01-10T06:44:01.852Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c4/2e7908915c0e32ca636b92e4e4a3bdec4cb1e7eb0f8aedf1ed3c68a0d8cd/numpy-2.4.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d558123217a83b2d1ba316b986e9248a1ed1971ad495963d555ccd75dcb1556", size = 14418963, upload-time = "2026-01-10T06:44:04.047Z" }, + { url = "https://files.pythonhosted.org/packages/eb/c0/3ed5083d94e7ffd7c404e54619c088e11f2e1939a9544f5397f4adb1b8ba/numpy-2.4.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f44de05659b67d20499cbc96d49f2650769afcb398b79b324bb6e297bfe3844", size = 16363811, upload-time = "2026-01-10T06:44:06.207Z" }, + { url = "https://files.pythonhosted.org/packages/0e/68/42b66f1852bf525050a67315a4fb94586ab7e9eaa541b1bef530fab0c5dd/numpy-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:69e7419c9012c4aaf695109564e3387f1259f001b4326dfa55907b098af082d3", size = 16197643, upload-time = "2026-01-10T06:44:08.33Z" }, + { url = "https://files.pythonhosted.org/packages/d2/40/e8714fc933d85f82c6bfc7b998a0649ad9769a32f3494ba86598aaf18a48/numpy-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2ffd257026eb1b34352e749d7cc1678b5eeec3e329ad8c9965a797e08ccba205", size = 18289601, upload-time = "2026-01-10T06:44:10.841Z" }, + { url = "https://files.pythonhosted.org/packages/80/9a/0d44b468cad50315127e884802351723daca7cf1c98d102929468c81d439/numpy-2.4.1-cp314-cp314-win32.whl", hash = "sha256:727c6c3275ddefa0dc078524a85e064c057b4f4e71ca5ca29a19163c607be745", size = 6005722, upload-time = "2026-01-10T06:44:13.332Z" }, + { url = "https://files.pythonhosted.org/packages/7e/bb/c6513edcce5a831810e2dddc0d3452ce84d208af92405a0c2e58fd8e7881/numpy-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:7d5d7999df434a038d75a748275cd6c0094b0ecdb0837342b332a82defc4dc4d", size = 12438590, upload-time = "2026-01-10T06:44:15.006Z" }, + { url = "https://files.pythonhosted.org/packages/e9/da/a598d5cb260780cf4d255102deba35c1d072dc028c4547832f45dd3323a8/numpy-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:ce9ce141a505053b3c7bce3216071f3bf5c182b8b28930f14cd24d43932cd2df", size = 10596180, upload-time = "2026-01-10T06:44:17.386Z" }, + { url = "https://files.pythonhosted.org/packages/de/bc/ea3f2c96fcb382311827231f911723aeff596364eb6e1b6d1d91128aa29b/numpy-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4e53170557d37ae404bf8d542ca5b7c629d6efa1117dac6a83e394142ea0a43f", size = 12498774, upload-time = "2026-01-10T06:44:19.467Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ab/ef9d939fe4a812648c7a712610b2ca6140b0853c5efea361301006c02ae5/numpy-2.4.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:a73044b752f5d34d4232f25f18160a1cc418ea4507f5f11e299d8ac36875f8a0", size = 5327274, upload-time = "2026-01-10T06:44:23.189Z" }, + { url = "https://files.pythonhosted.org/packages/bd/31/d381368e2a95c3b08b8cf7faac6004849e960f4a042d920337f71cef0cae/numpy-2.4.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:fb1461c99de4d040666ca0444057b06541e5642f800b71c56e6ea92d6a853a0c", size = 6648306, upload-time = "2026-01-10T06:44:25.012Z" }, + { url = "https://files.pythonhosted.org/packages/c8/e5/0989b44ade47430be6323d05c23207636d67d7362a1796ccbccac6773dd2/numpy-2.4.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423797bdab2eeefbe608d7c1ec7b2b4fd3c58d51460f1ee26c7500a1d9c9ee93", size = 14464653, upload-time = "2026-01-10T06:44:26.706Z" }, + { url = "https://files.pythonhosted.org/packages/10/a7/cfbe475c35371cae1358e61f20c5f075badc18c4797ab4354140e1d283cf/numpy-2.4.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:52b5f61bdb323b566b528899cc7db2ba5d1015bda7ea811a8bcf3c89c331fa42", size = 16405144, upload-time = "2026-01-10T06:44:29.378Z" }, + { url = "https://files.pythonhosted.org/packages/f8/a3/0c63fe66b534888fa5177cc7cef061541064dbe2b4b60dcc60ffaf0d2157/numpy-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42d7dd5fa36d16d52a84f821eb96031836fd405ee6955dd732f2023724d0aa01", size = 16247425, upload-time = "2026-01-10T06:44:31.721Z" }, + { url = "https://files.pythonhosted.org/packages/6b/2b/55d980cfa2c93bd40ff4c290bf824d792bd41d2fe3487b07707559071760/numpy-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7b6b5e28bbd47b7532698e5db2fe1db693d84b58c254e4389d99a27bb9b8f6b", size = 18330053, upload-time = "2026-01-10T06:44:34.617Z" }, + { url = "https://files.pythonhosted.org/packages/23/12/8b5fc6b9c487a09a7957188e0943c9ff08432c65e34567cabc1623b03a51/numpy-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:5de60946f14ebe15e713a6f22850c2372fa72f4ff9a432ab44aa90edcadaa65a", size = 6152482, upload-time = "2026-01-10T06:44:36.798Z" }, + { url = "https://files.pythonhosted.org/packages/00/a5/9f8ca5856b8940492fc24fbe13c1bc34d65ddf4079097cf9e53164d094e1/numpy-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8f085da926c0d491ffff3096f91078cc97ea67e7e6b65e490bc8dcda65663be2", size = 12627117, upload-time = "2026-01-10T06:44:38.828Z" }, + { url = "https://files.pythonhosted.org/packages/ad/0d/eca3d962f9eef265f01a8e0d20085c6dd1f443cbffc11b6dede81fd82356/numpy-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:6436cffb4f2bf26c974344439439c95e152c9a527013f26b3577be6c2ca64295", size = 10667121, upload-time = "2026-01-10T06:44:41.644Z" }, + { url = "https://files.pythonhosted.org/packages/1e/48/d86f97919e79314a1cdee4c832178763e6e98e623e123d0bada19e92c15a/numpy-2.4.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8ad35f20be147a204e28b6a0575fbf3540c5e5f802634d4258d55b1ff5facce1", size = 16822202, upload-time = "2026-01-10T06:44:43.738Z" }, + { url = "https://files.pythonhosted.org/packages/51/e9/1e62a7f77e0f37dcfb0ad6a9744e65df00242b6ea37dfafb55debcbf5b55/numpy-2.4.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8097529164c0f3e32bb89412a0905d9100bf434d9692d9fc275e18dcf53c9344", size = 12569985, upload-time = "2026-01-10T06:44:45.945Z" }, + { url = "https://files.pythonhosted.org/packages/c7/7e/914d54f0c801342306fdcdce3e994a56476f1b818c46c47fc21ae968088c/numpy-2.4.1-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:ea66d2b41ca4a1630aae5507ee0a71647d3124d1741980138aa8f28f44dac36e", size = 5398484, upload-time = "2026-01-10T06:44:48.012Z" }, + { url = "https://files.pythonhosted.org/packages/1c/d8/9570b68584e293a33474e7b5a77ca404f1dcc655e40050a600dee81d27fb/numpy-2.4.1-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:d3f8f0df9f4b8be57b3bf74a1d087fec68f927a2fab68231fdb442bf2c12e426", size = 6713216, upload-time = "2026-01-10T06:44:49.725Z" }, + { url = "https://files.pythonhosted.org/packages/33/9b/9dd6e2db8d49eb24f86acaaa5258e5f4c8ed38209a4ee9de2d1a0ca25045/numpy-2.4.1-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2023ef86243690c2791fd6353e5b4848eedaa88ca8a2d129f462049f6d484696", size = 14538937, upload-time = "2026-01-10T06:44:51.498Z" }, + { url = "https://files.pythonhosted.org/packages/53/87/d5bd995b0f798a37105b876350d346eea5838bd8f77ea3d7a48392f3812b/numpy-2.4.1-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8361ea4220d763e54cff2fbe7d8c93526b744f7cd9ddab47afeff7e14e8503be", size = 16479830, upload-time = "2026-01-10T06:44:53.931Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c7/b801bf98514b6ae6475e941ac05c58e6411dd863ea92916bfd6d510b08c1/numpy-2.4.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:4f1b68ff47680c2925f8063402a693ede215f0257f02596b1318ecdfb1d79e33", size = 12492579, upload-time = "2026-01-10T06:44:57.094Z" }, +] + [[package]] name = "packaging" version = "25.0" @@ -504,6 +735,147 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] +[[package]] +name = "pandas" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +dependencies = [ + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "python-dateutil", marker = "python_full_version < '3.11'" }, + { name = "pytz", marker = "python_full_version < '3.11'" }, + { name = "tzdata", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/f7/f425a00df4fcc22b292c6895c6831c0c8ae1d9fac1e024d16f98a9ce8749/pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c", size = 11555763, upload-time = "2025-09-29T23:16:53.287Z" }, + { url = "https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a", size = 10801217, upload-time = "2025-09-29T23:17:04.522Z" }, + { url = "https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1", size = 12148791, upload-time = "2025-09-29T23:17:18.444Z" }, + { url = "https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838", size = 12769373, upload-time = "2025-09-29T23:17:35.846Z" }, + { url = "https://files.pythonhosted.org/packages/df/91/82cc5169b6b25440a7fc0ef3a694582418d875c8e3ebf796a6d6470aa578/pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250", size = 13200444, upload-time = "2025-09-29T23:17:49.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/ae/89b3283800ab58f7af2952704078555fa60c807fff764395bb57ea0b0dbd/pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4", size = 13858459, upload-time = "2025-09-29T23:18:03.722Z" }, + { url = "https://files.pythonhosted.org/packages/85/72/530900610650f54a35a19476eca5104f38555afccda1aa11a92ee14cb21d/pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826", size = 11346086, upload-time = "2025-09-29T23:18:18.505Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523", size = 11578790, upload-time = "2025-09-29T23:18:30.065Z" }, + { url = "https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45", size = 10833831, upload-time = "2025-09-29T23:38:56.071Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e4/de154cbfeee13383ad58d23017da99390b91d73f8c11856f2095e813201b/pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66", size = 12199267, upload-time = "2025-09-29T23:18:41.627Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b", size = 12789281, upload-time = "2025-09-29T23:18:56.834Z" }, + { url = "https://files.pythonhosted.org/packages/f2/00/a5ac8c7a0e67fd1a6059e40aa08fa1c52cc00709077d2300e210c3ce0322/pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791", size = 13240453, upload-time = "2025-09-29T23:19:09.247Z" }, + { url = "https://files.pythonhosted.org/packages/27/4d/5c23a5bc7bd209231618dd9e606ce076272c9bc4f12023a70e03a86b4067/pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151", size = 13890361, upload-time = "2025-09-29T23:19:25.342Z" }, + { url = "https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c", size = 11348702, upload-time = "2025-09-29T23:19:38.296Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" }, + { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" }, + { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" }, + { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" }, + { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" }, + { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" }, + { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" }, + { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" }, + { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" }, + { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" }, + { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" }, + { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" }, + { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" }, + { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" }, + { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" }, + { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" }, + { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" }, + { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" }, + { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" }, + { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" }, + { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" }, + { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" }, + { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" }, + { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" }, + { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" }, + { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" }, + { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" }, + { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" }, + { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" }, + { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" }, + { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" }, + { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" }, + { url = "https://files.pythonhosted.org/packages/56/b4/52eeb530a99e2a4c55ffcd352772b599ed4473a0f892d127f4147cf0f88e/pandas-2.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c503ba5216814e295f40711470446bc3fd00f0faea8a086cbc688808e26f92a2", size = 11567720, upload-time = "2025-09-29T23:33:06.209Z" }, + { url = "https://files.pythonhosted.org/packages/48/4a/2d8b67632a021bced649ba940455ed441ca854e57d6e7658a6024587b083/pandas-2.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a637c5cdfa04b6d6e2ecedcb81fc52ffb0fd78ce2ebccc9ea964df9f658de8c8", size = 10810302, upload-time = "2025-09-29T23:33:35.846Z" }, + { url = "https://files.pythonhosted.org/packages/13/e6/d2465010ee0569a245c975dc6967b801887068bc893e908239b1f4b6c1ac/pandas-2.3.3-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:854d00d556406bffe66a4c0802f334c9ad5a96b4f1f868adf036a21b11ef13ff", size = 12154874, upload-time = "2025-09-29T23:33:49.939Z" }, + { url = "https://files.pythonhosted.org/packages/1f/18/aae8c0aa69a386a3255940e9317f793808ea79d0a525a97a903366bb2569/pandas-2.3.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf1f8a81d04ca90e32a0aceb819d34dbd378a98bf923b6398b9a3ec0bf44de29", size = 12790141, upload-time = "2025-09-29T23:34:05.655Z" }, + { url = "https://files.pythonhosted.org/packages/f7/26/617f98de789de00c2a444fbe6301bb19e66556ac78cff933d2c98f62f2b4/pandas-2.3.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:23ebd657a4d38268c7dfbdf089fbc31ea709d82e4923c5ffd4fbd5747133ce73", size = 13208697, upload-time = "2025-09-29T23:34:21.835Z" }, + { url = "https://files.pythonhosted.org/packages/b9/fb/25709afa4552042bd0e15717c75e9b4a2294c3dc4f7e6ea50f03c5136600/pandas-2.3.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5554c929ccc317d41a5e3d1234f3be588248e61f08a74dd17c9eabb535777dc9", size = 13879233, upload-time = "2025-09-29T23:34:35.079Z" }, + { url = "https://files.pythonhosted.org/packages/98/af/7be05277859a7bc399da8ba68b88c96b27b48740b6cf49688899c6eb4176/pandas-2.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:d3e28b3e83862ccf4d85ff19cf8c20b2ae7e503881711ff2d534dc8f761131aa", size = 11359119, upload-time = "2025-09-29T23:34:46.339Z" }, +] + +[[package]] +name = "pandas" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, + { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/da/b1dc0481ab8d55d0f46e343cfe67d4551a0e14fcee52bd38ca1bd73258d8/pandas-3.0.0.tar.gz", hash = "sha256:0facf7e87d38f721f0af46fe70d97373a37701b1c09f7ed7aeeb292ade5c050f", size = 4633005, upload-time = "2026-01-21T15:52:04.726Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/1e/b184654a856e75e975a6ee95d6577b51c271cd92cb2b020c9378f53e0032/pandas-3.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d64ce01eb9cdca96a15266aa679ae50212ec52757c79204dbc7701a222401850", size = 10313247, upload-time = "2026-01-21T15:50:15.775Z" }, + { url = "https://files.pythonhosted.org/packages/dd/5e/e04a547ad0f0183bf151fd7c7a477468e3b85ff2ad231c566389e6cc9587/pandas-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:613e13426069793aa1ec53bdcc3b86e8d32071daea138bbcf4fa959c9cdaa2e2", size = 9913131, upload-time = "2026-01-21T15:50:18.611Z" }, + { url = "https://files.pythonhosted.org/packages/a2/93/bb77bfa9fc2aba9f7204db807d5d3fb69832ed2854c60ba91b4c65ba9219/pandas-3.0.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0192fee1f1a8e743b464a6607858ee4b071deb0b118eb143d71c2a1d170996d5", size = 10741925, upload-time = "2026-01-21T15:50:21.058Z" }, + { url = "https://files.pythonhosted.org/packages/62/fb/89319812eb1d714bfc04b7f177895caeba8ab4a37ef6712db75ed786e2e0/pandas-3.0.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0b853319dec8d5e0c8b875374c078ef17f2269986a78168d9bd57e49bf650ae", size = 11245979, upload-time = "2026-01-21T15:50:23.413Z" }, + { url = "https://files.pythonhosted.org/packages/a9/63/684120486f541fc88da3862ed31165b3b3e12b6a1c7b93be4597bc84e26c/pandas-3.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:707a9a877a876c326ae2cb640fbdc4ef63b0a7b9e2ef55c6df9942dcee8e2af9", size = 11756337, upload-time = "2026-01-21T15:50:25.932Z" }, + { url = "https://files.pythonhosted.org/packages/39/92/7eb0ad232312b59aec61550c3c81ad0743898d10af5df7f80bc5e5065416/pandas-3.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:afd0aa3d0b5cda6e0b8ffc10dbcca3b09ef3cbcd3fe2b27364f85fdc04e1989d", size = 12325517, upload-time = "2026-01-21T15:50:27.952Z" }, + { url = "https://files.pythonhosted.org/packages/51/27/bf9436dd0a4fc3130acec0828951c7ef96a0631969613a9a35744baf27f6/pandas-3.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:113b4cca2614ff7e5b9fee9b6f066618fe73c5a83e99d721ffc41217b2bf57dd", size = 9881576, upload-time = "2026-01-21T15:50:30.149Z" }, + { url = "https://files.pythonhosted.org/packages/e7/2b/c618b871fce0159fd107516336e82891b404e3f340821853c2fc28c7830f/pandas-3.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c14837eba8e99a8da1527c0280bba29b0eb842f64aa94982c5e21227966e164b", size = 9140807, upload-time = "2026-01-21T15:50:32.308Z" }, + { url = "https://files.pythonhosted.org/packages/0b/38/db33686f4b5fa64d7af40d96361f6a4615b8c6c8f1b3d334eee46ae6160e/pandas-3.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9803b31f5039b3c3b10cc858c5e40054adb4b29b4d81cb2fd789f4121c8efbcd", size = 10334013, upload-time = "2026-01-21T15:50:34.771Z" }, + { url = "https://files.pythonhosted.org/packages/a5/7b/9254310594e9774906bacdd4e732415e1f86ab7dbb4b377ef9ede58cd8ec/pandas-3.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14c2a4099cd38a1d18ff108168ea417909b2dea3bd1ebff2ccf28ddb6a74d740", size = 9874154, upload-time = "2026-01-21T15:50:36.67Z" }, + { url = "https://files.pythonhosted.org/packages/63/d4/726c5a67a13bc66643e66d2e9ff115cead482a44fc56991d0c4014f15aaf/pandas-3.0.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d257699b9a9960e6125686098d5714ac59d05222bef7a5e6af7a7fd87c650801", size = 10384433, upload-time = "2026-01-21T15:50:39.132Z" }, + { url = "https://files.pythonhosted.org/packages/bf/2e/9211f09bedb04f9832122942de8b051804b31a39cfbad199a819bb88d9f3/pandas-3.0.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:69780c98f286076dcafca38d8b8eee1676adf220199c0a39f0ecbf976b68151a", size = 10864519, upload-time = "2026-01-21T15:50:41.043Z" }, + { url = "https://files.pythonhosted.org/packages/00/8d/50858522cdc46ac88b9afdc3015e298959a70a08cd21e008a44e9520180c/pandas-3.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4a66384f017240f3858a4c8a7cf21b0591c3ac885cddb7758a589f0f71e87ebb", size = 11394124, upload-time = "2026-01-21T15:50:43.377Z" }, + { url = "https://files.pythonhosted.org/packages/86/3f/83b2577db02503cd93d8e95b0f794ad9d4be0ba7cb6c8bcdcac964a34a42/pandas-3.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be8c515c9bc33989d97b89db66ea0cececb0f6e3c2a87fcc8b69443a6923e95f", size = 11920444, upload-time = "2026-01-21T15:50:45.932Z" }, + { url = "https://files.pythonhosted.org/packages/64/2d/4f8a2f192ed12c90a0aab47f5557ece0e56b0370c49de9454a09de7381b2/pandas-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:a453aad8c4f4e9f166436994a33884442ea62aa8b27d007311e87521b97246e1", size = 9730970, upload-time = "2026-01-21T15:50:47.962Z" }, + { url = "https://files.pythonhosted.org/packages/d4/64/ff571be435cf1e643ca98d0945d76732c0b4e9c37191a89c8550b105eed1/pandas-3.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:da768007b5a33057f6d9053563d6b74dd6d029c337d93c6d0d22a763a5c2ecc0", size = 9041950, upload-time = "2026-01-21T15:50:50.422Z" }, + { url = "https://files.pythonhosted.org/packages/6f/fa/7f0ac4ca8877c57537aaff2a842f8760e630d8e824b730eb2e859ffe96ca/pandas-3.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b78d646249b9a2bc191040988c7bb524c92fa8534fb0898a0741d7e6f2ffafa6", size = 10307129, upload-time = "2026-01-21T15:50:52.877Z" }, + { url = "https://files.pythonhosted.org/packages/6f/11/28a221815dcea4c0c9414dfc845e34a84a6a7dabc6da3194498ed5ba4361/pandas-3.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bc9cba7b355cb4162442a88ce495e01cb605f17ac1e27d6596ac963504e0305f", size = 9850201, upload-time = "2026-01-21T15:50:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/ba/da/53bbc8c5363b7e5bd10f9ae59ab250fc7a382ea6ba08e4d06d8694370354/pandas-3.0.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c9a1a149aed3b6c9bf246033ff91e1b02d529546c5d6fb6b74a28fea0cf4c70", size = 10354031, upload-time = "2026-01-21T15:50:57.463Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a3/51e02ebc2a14974170d51e2410dfdab58870ea9bcd37cda15bd553d24dc4/pandas-3.0.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95683af6175d884ee89471842acfca29172a85031fccdabc35e50c0984470a0e", size = 10861165, upload-time = "2026-01-21T15:50:59.32Z" }, + { url = "https://files.pythonhosted.org/packages/a5/fe/05a51e3cac11d161472b8297bd41723ea98013384dd6d76d115ce3482f9b/pandas-3.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1fbbb5a7288719e36b76b4f18d46ede46e7f916b6c8d9915b756b0a6c3f792b3", size = 11359359, upload-time = "2026-01-21T15:51:02.014Z" }, + { url = "https://files.pythonhosted.org/packages/ee/56/ba620583225f9b85a4d3e69c01df3e3870659cc525f67929b60e9f21dcd1/pandas-3.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8e8b9808590fa364416b49b2a35c1f4cf2785a6c156935879e57f826df22038e", size = 11912907, upload-time = "2026-01-21T15:51:05.175Z" }, + { url = "https://files.pythonhosted.org/packages/c9/8c/c6638d9f67e45e07656b3826405c5cc5f57f6fd07c8b2572ade328c86e22/pandas-3.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:98212a38a709feb90ae658cb6227ea3657c22ba8157d4b8f913cd4c950de5e7e", size = 9732138, upload-time = "2026-01-21T15:51:07.569Z" }, + { url = "https://files.pythonhosted.org/packages/7b/bf/bd1335c3bf1770b6d8fed2799993b11c4971af93bb1b729b9ebbc02ca2ec/pandas-3.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:177d9df10b3f43b70307a149d7ec49a1229a653f907aa60a48f1877d0e6be3be", size = 9033568, upload-time = "2026-01-21T15:51:09.484Z" }, + { url = "https://files.pythonhosted.org/packages/8e/c6/f5e2171914d5e29b9171d495344097d54e3ffe41d2d85d8115baba4dc483/pandas-3.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2713810ad3806767b89ad3b7b69ba153e1c6ff6d9c20f9c2140379b2a98b6c98", size = 10741936, upload-time = "2026-01-21T15:51:11.693Z" }, + { url = "https://files.pythonhosted.org/packages/51/88/9a0164f99510a1acb9f548691f022c756c2314aad0d8330a24616c14c462/pandas-3.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:15d59f885ee5011daf8335dff47dcb8a912a27b4ad7826dc6cbe809fd145d327", size = 10393884, upload-time = "2026-01-21T15:51:14.197Z" }, + { url = "https://files.pythonhosted.org/packages/e0/53/b34d78084d88d8ae2b848591229da8826d1e65aacf00b3abe34023467648/pandas-3.0.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24e6547fb64d2c92665dd2adbfa4e85fa4fd70a9c070e7cfb03b629a0bbab5eb", size = 10310740, upload-time = "2026-01-21T15:51:16.093Z" }, + { url = "https://files.pythonhosted.org/packages/5b/d3/bee792e7c3d6930b74468d990604325701412e55d7aaf47460a22311d1a5/pandas-3.0.0-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:48ee04b90e2505c693d3f8e8f524dab8cb8aaf7ddcab52c92afa535e717c4812", size = 10700014, upload-time = "2026-01-21T15:51:18.818Z" }, + { url = "https://files.pythonhosted.org/packages/55/db/2570bc40fb13aaed1cbc3fbd725c3a60ee162477982123c3adc8971e7ac1/pandas-3.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:66f72fb172959af42a459e27a8d8d2c7e311ff4c1f7db6deb3b643dbc382ae08", size = 11323737, upload-time = "2026-01-21T15:51:20.784Z" }, + { url = "https://files.pythonhosted.org/packages/bc/2e/297ac7f21c8181b62a4cccebad0a70caf679adf3ae5e83cb676194c8acc3/pandas-3.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4a4a400ca18230976724a5066f20878af785f36c6756e498e94c2a5e5d57779c", size = 11771558, upload-time = "2026-01-21T15:51:22.977Z" }, + { url = "https://files.pythonhosted.org/packages/0a/46/e1c6876d71c14332be70239acce9ad435975a80541086e5ffba2f249bcf6/pandas-3.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:940eebffe55528074341a5a36515f3e4c5e25e958ebbc764c9502cfc35ba3faa", size = 10473771, upload-time = "2026-01-21T15:51:25.285Z" }, + { url = "https://files.pythonhosted.org/packages/c0/db/0270ad9d13c344b7a36fa77f5f8344a46501abf413803e885d22864d10bf/pandas-3.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:597c08fb9fef0edf1e4fa2f9828dd27f3d78f9b8c9b4a748d435ffc55732310b", size = 10312075, upload-time = "2026-01-21T15:51:28.5Z" }, + { url = "https://files.pythonhosted.org/packages/09/9f/c176f5e9717f7c91becfe0f55a52ae445d3f7326b4a2cf355978c51b7913/pandas-3.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:447b2d68ac5edcbf94655fe909113a6dba6ef09ad7f9f60c80477825b6c489fe", size = 9900213, upload-time = "2026-01-21T15:51:30.955Z" }, + { url = "https://files.pythonhosted.org/packages/d9/e7/63ad4cc10b257b143e0a5ebb04304ad806b4e1a61c5da25f55896d2ca0f4/pandas-3.0.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:debb95c77ff3ed3ba0d9aa20c3a2f19165cc7956362f9873fce1ba0a53819d70", size = 10428768, upload-time = "2026-01-21T15:51:33.018Z" }, + { url = "https://files.pythonhosted.org/packages/9e/0e/4e4c2d8210f20149fd2248ef3fff26623604922bd564d915f935a06dd63d/pandas-3.0.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fedabf175e7cd82b69b74c30adbaa616de301291a5231138d7242596fc296a8d", size = 10882954, upload-time = "2026-01-21T15:51:35.287Z" }, + { url = "https://files.pythonhosted.org/packages/c6/60/c9de8ac906ba1f4d2250f8a951abe5135b404227a55858a75ad26f84db47/pandas-3.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:412d1a89aab46889f3033a386912efcdfa0f1131c5705ff5b668dda88305e986", size = 11430293, upload-time = "2026-01-21T15:51:37.57Z" }, + { url = "https://files.pythonhosted.org/packages/a1/69/806e6637c70920e5787a6d6896fd707f8134c2c55cd761e7249a97b7dc5a/pandas-3.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e979d22316f9350c516479dd3a92252be2937a9531ed3a26ec324198a99cdd49", size = 11952452, upload-time = "2026-01-21T15:51:39.618Z" }, + { url = "https://files.pythonhosted.org/packages/cb/de/918621e46af55164c400ab0ef389c9d969ab85a43d59ad1207d4ddbe30a5/pandas-3.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:083b11415b9970b6e7888800c43c82e81a06cd6b06755d84804444f0007d6bb7", size = 9851081, upload-time = "2026-01-21T15:51:41.758Z" }, + { url = "https://files.pythonhosted.org/packages/91/a1/3562a18dd0bd8c73344bfa26ff90c53c72f827df119d6d6b1dacc84d13e3/pandas-3.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:5db1e62cb99e739fa78a28047e861b256d17f88463c76b8dafc7c1338086dca8", size = 9174610, upload-time = "2026-01-21T15:51:44.312Z" }, + { url = "https://files.pythonhosted.org/packages/ce/26/430d91257eaf366f1737d7a1c158677caaf6267f338ec74e3a1ec444111c/pandas-3.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:697b8f7d346c68274b1b93a170a70974cdc7d7354429894d5927c1effdcccd73", size = 10761999, upload-time = "2026-01-21T15:51:46.899Z" }, + { url = "https://files.pythonhosted.org/packages/ec/1a/954eb47736c2b7f7fe6a9d56b0cb6987773c00faa3c6451a43db4beb3254/pandas-3.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8cb3120f0d9467ed95e77f67a75e030b67545bcfa08964e349252d674171def2", size = 10410279, upload-time = "2026-01-21T15:51:48.89Z" }, + { url = "https://files.pythonhosted.org/packages/20/fc/b96f3a5a28b250cd1b366eb0108df2501c0f38314a00847242abab71bb3a/pandas-3.0.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33fd3e6baa72899746b820c31e4b9688c8e1b7864d7aec2de7ab5035c285277a", size = 10330198, upload-time = "2026-01-21T15:51:51.015Z" }, + { url = "https://files.pythonhosted.org/packages/90/b3/d0e2952f103b4fbef1ef22d0c2e314e74fc9064b51cee30890b5e3286ee6/pandas-3.0.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8942e333dc67ceda1095227ad0febb05a3b36535e520154085db632c40ad084", size = 10728513, upload-time = "2026-01-21T15:51:53.387Z" }, + { url = "https://files.pythonhosted.org/packages/76/81/832894f286df828993dc5fd61c63b231b0fb73377e99f6c6c369174cf97e/pandas-3.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:783ac35c4d0fe0effdb0d67161859078618b1b6587a1af15928137525217a721", size = 11345550, upload-time = "2026-01-21T15:51:55.329Z" }, + { url = "https://files.pythonhosted.org/packages/34/a0/ed160a00fb4f37d806406bc0a79a8b62fe67f29d00950f8d16203ff3409b/pandas-3.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:125eb901e233f155b268bbef9abd9afb5819db74f0e677e89a61b246228c71ac", size = 11799386, upload-time = "2026-01-21T15:51:57.457Z" }, + { url = "https://files.pythonhosted.org/packages/36/c8/2ac00d7255252c5e3cf61b35ca92ca25704b0188f7454ca4aec08a33cece/pandas-3.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b86d113b6c109df3ce0ad5abbc259fe86a1bd4adfd4a31a89da42f84f65509bb", size = 10873041, upload-time = "2026-01-21T15:52:00.034Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3f/a80ac00acbc6b35166b42850e98a4f466e2c0d9c64054161ba9620f95680/pandas-3.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:1c39eab3ad38f2d7a249095f0a3d8f8c22cc0f847e98ccf5bbe732b272e2d9fa", size = 9441003, upload-time = "2026-01-21T15:52:02.281Z" }, +] + [[package]] name = "pathspec" version = "1.0.3" @@ -530,7 +902,13 @@ name = "platformdirs" version = "4.5.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } wheels = [ @@ -546,6 +924,84 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] +[[package]] +name = "polars" +version = "1.36.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "polars-runtime-32", version = "1.36.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/dc/56f2a90c79a2cb13f9e956eab6385effe54216ae7a2068b3a6406bae4345/polars-1.36.1.tar.gz", hash = "sha256:12c7616a2305559144711ab73eaa18814f7aa898c522e7645014b68f1432d54c", size = 711993, upload-time = "2025-12-10T01:14:53.033Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/c6/36a1b874036b49893ecae0ac44a2f63d1a76e6212631a5b2f50a86e0e8af/polars-1.36.1-py3-none-any.whl", hash = "sha256:853c1bbb237add6a5f6d133c15094a9b727d66dd6a4eb91dbb07cdb056b2b8ef", size = 802429, upload-time = "2025-12-10T01:13:53.838Z" }, +] + +[[package]] +name = "polars" +version = "1.37.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "polars-runtime-32", version = "1.37.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/84/ae/dfebf31b9988c20998140b54d5b521f64ce08879f2c13d9b4d44d7c87e32/polars-1.37.1.tar.gz", hash = "sha256:0309e2a4633e712513401964b4d95452f124ceabf7aec6db50affb9ced4a274e", size = 715572, upload-time = "2026-01-12T23:27:03.267Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/75/ec73e38812bca7c2240aff481b9ddff20d1ad2f10dee4b3353f5eeaacdab/polars-1.37.1-py3-none-any.whl", hash = "sha256:377fed8939a2f1223c1563cfabdc7b4a3d6ff846efa1f2ddeb8644fafd9b1aff", size = 805749, upload-time = "2026-01-12T23:25:48.595Z" }, +] + +[[package]] +name = "polars-runtime-32" +version = "1.36.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/31/df/597c0ef5eb8d761a16d72327846599b57c5d40d7f9e74306fc154aba8c37/polars_runtime_32-1.36.1.tar.gz", hash = "sha256:201c2cfd80ceb5d5cd7b63085b5fd08d6ae6554f922bcb941035e39638528a09", size = 2788751, upload-time = "2025-12-10T01:14:54.172Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/ea/871129a2d296966c0925b078a9a93c6c5e7facb1c5eebfcd3d5811aeddc1/polars_runtime_32-1.36.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:327b621ca82594f277751f7e23d4b939ebd1be18d54b4cdf7a2f8406cecc18b2", size = 43494311, upload-time = "2025-12-10T01:13:56.096Z" }, + { url = "https://files.pythonhosted.org/packages/d8/76/0038210ad1e526ce5bb2933b13760d6b986b3045eccc1338e661bd656f77/polars_runtime_32-1.36.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ab0d1f23084afee2b97de8c37aa3e02ec3569749ae39571bd89e7a8b11ae9e83", size = 39300602, upload-time = "2025-12-10T01:13:59.366Z" }, + { url = "https://files.pythonhosted.org/packages/54/1e/2707bee75a780a953a77a2c59829ee90ef55708f02fc4add761c579bf76e/polars_runtime_32-1.36.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:899b9ad2e47ceb31eb157f27a09dbc2047efbf4969a923a6b1ba7f0412c3e64c", size = 44511780, upload-time = "2025-12-10T01:14:02.285Z" }, + { url = "https://files.pythonhosted.org/packages/11/b2/3fede95feee441be64b4bcb32444679a8fbb7a453a10251583053f6efe52/polars_runtime_32-1.36.1-cp39-abi3-manylinux_2_24_aarch64.whl", hash = "sha256:d9d077bb9df711bc635a86540df48242bb91975b353e53ef261c6fae6cb0948f", size = 40688448, upload-time = "2025-12-10T01:14:05.131Z" }, + { url = "https://files.pythonhosted.org/packages/05/0f/e629713a72999939b7b4bfdbf030a32794db588b04fdf3dc977dd8ea6c53/polars_runtime_32-1.36.1-cp39-abi3-win_amd64.whl", hash = "sha256:cc17101f28c9a169ff8b5b8d4977a3683cd403621841623825525f440b564cf0", size = 44464898, upload-time = "2025-12-10T01:14:08.296Z" }, + { url = "https://files.pythonhosted.org/packages/d1/d8/a12e6aa14f63784cead437083319ec7cece0d5bb9a5bfe7678cc6578b52a/polars_runtime_32-1.36.1-cp39-abi3-win_arm64.whl", hash = "sha256:809e73857be71250141225ddd5d2b30c97e6340aeaa0d445f930e01bef6888dc", size = 39798896, upload-time = "2025-12-10T01:14:11.568Z" }, +] + +[[package]] +name = "polars-runtime-32" +version = "1.37.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/40/0b/addabe5e8d28a5a4c9887a08907be7ddc3fce892dc38f37d14b055438a57/polars_runtime_32-1.37.1.tar.gz", hash = "sha256:68779d4a691da20a5eb767d74165a8f80a2bdfbde4b54acf59af43f7fa028d8f", size = 2818945, upload-time = "2026-01-12T23:27:04.653Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/a2/e828ea9f845796de02d923edb790e408ca0b560cd68dbd74bb99a1b3c461/polars_runtime_32-1.37.1-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:0b8d4d73ea9977d3731927740e59d814647c5198bdbe359bcf6a8bfce2e79771", size = 43499912, upload-time = "2026-01-12T23:25:51.182Z" }, + { url = "https://files.pythonhosted.org/packages/7e/46/81b71b7aa9e3703ee6e4ef1f69a87e40f58ea7c99212bf49a95071e99c8c/polars_runtime_32-1.37.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:c682bf83f5f352e5e02f5c16c652c48ca40442f07b236f30662b22217320ce76", size = 39695707, upload-time = "2026-01-12T23:25:54.289Z" }, + { url = "https://files.pythonhosted.org/packages/81/2e/20009d1fde7ee919e24040f5c87cb9d0e4f8e3f109b74ba06bc10c02459c/polars_runtime_32-1.37.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc82b5bbe70ca1a4b764eed1419f6336752d6ba9fc1245388d7f8b12438afa2c", size = 41467034, upload-time = "2026-01-12T23:25:56.925Z" }, + { url = "https://files.pythonhosted.org/packages/eb/21/9b55bea940524324625b1e8fd96233290303eb1bf2c23b54573487bbbc25/polars_runtime_32-1.37.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8362d11ac5193b994c7e9048ffe22ccfb976699cfbf6e128ce0302e06728894", size = 45142711, upload-time = "2026-01-12T23:26:00.817Z" }, + { url = "https://files.pythonhosted.org/packages/8c/25/c5f64461aeccdac6834a89f826d051ccd3b4ce204075e562c87a06ed2619/polars_runtime_32-1.37.1-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:04f5d5a2f013dca7391b7d8e7672fa6d37573a87f1d45d3dd5f0d9b5565a4b0f", size = 41638564, upload-time = "2026-01-12T23:26:04.186Z" }, + { url = "https://files.pythonhosted.org/packages/35/af/509d3cf6c45e764ccf856beaae26fc34352f16f10f94a7839b1042920a73/polars_runtime_32-1.37.1-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fbfde7c0ca8209eeaed546e4a32cca1319189aa61c5f0f9a2b4494262bd0c689", size = 44721136, upload-time = "2026-01-12T23:26:07.088Z" }, + { url = "https://files.pythonhosted.org/packages/af/d1/5c0a83a625f72beef59394bebc57d12637997632a4f9d3ab2ffc2cc62bbf/polars_runtime_32-1.37.1-cp310-abi3-win_amd64.whl", hash = "sha256:da3d3642ae944e18dd17109d2a3036cb94ce50e5495c5023c77b1599d4c861bc", size = 44948288, upload-time = "2026-01-12T23:26:10.214Z" }, + { url = "https://files.pythonhosted.org/packages/10/f3/061bb702465904b6502f7c9081daee34b09ccbaa4f8c94cf43a2a3b6dd6f/polars_runtime_32-1.37.1-cp310-abi3-win_arm64.whl", hash = "sha256:55f2c4847a8d2e267612f564de7b753a4bde3902eaabe7b436a0a4abf75949a0", size = 41001914, upload-time = "2026-01-12T23:26:12.997Z" }, +] + [[package]] name = "pydoc-markdown" version = "4.8.2" @@ -607,7 +1063,13 @@ name = "pytest" version = "9.0.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", ] dependencies = [ { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, @@ -623,6 +1085,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, ] +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "pytz" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, +] + [[package]] name = "pyyaml" version = "6.0.3" @@ -720,6 +1203,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e0/76/f963c61683a39084aa575f98089253e1e852a4417cb8a3a8a422923a5246/setuptools-80.10.1-py3-none-any.whl", hash = "sha256:fc30c51cbcb8199a219c12cc9c281b5925a4978d212f84229c909636d9f6984e", size = 1099859, upload-time = "2026-01-21T09:42:00.688Z" }, ] +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + [[package]] name = "tomli" version = "2.4.0" @@ -804,6 +1296,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] +[[package]] +name = "tzdata" +version = "2025.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" }, +] + [[package]] name = "urllib3" version = "2.6.3"