///|
/// A numeric dictionary key with its original scalar origin and object identity.
pub struct EngPrefixKey {
priv number : ScalarNumber
priv numeric_hash : Int64
}
///|
pub impl @debug.Debug for EngPrefixKey with fn to_repr(self) {
@debug.Repr::Repr(("EngPrefixKey", self.number))
}
///|
pub extend EngPrefixKey with @debug.Debug::{to_repr}
///|
pub impl Eq for EngPrefixKey with fn equal(self, other) {
physical_equal(self, other)
}
///|
pub extend EngPrefixKey with Eq::{equal, not_equal}
///|
fn eng_prefix_hash(number : ScalarNumber) -> Int64 {
let modulus = @bigint.BigInt::from_int64(2305843009213693951L)
let (magnitude, negative) = match number {
Integer(value) =>
(
if value < @bigint.BigInt::from_int(0) {
value.neg()
} else {
value
},
value < @bigint.BigInt::from_int(0),
)
Boolean(value) | NumpyBool(value) =>
(@bigint.BigInt::from_int(if value { 1 } else { 0 }), false)
NumpyInt64(value) => {
let value = @bigint.BigInt::from_int64(value)
(
if value < @bigint.BigInt::from_int(0) {
value.neg()
} else {
value
},
value < @bigint.BigInt::from_int(0),
)
}
Number(value) | NumpyFloat64(value) => {
if value.is_nan() {
return 0L
}
if value.is_inf() {
return if value < 0.0 { -314159L } else { 314159L }
}
let bits = value.reinterpret_as_uint64() & 0x7fffffffffffffffUL
let biased = ((bits >> 52) & 0x7ffUL).to_int()
let fraction = bits & 0xfffffffffffffUL
let significand = if biased == 0 {
fraction
} else {
fraction | 0x10000000000000UL
}
let exponent = if biased == 0 { -1074 } else { biased - 1075 }
// 2**61 == 1 modulo P, including negative binary exponents.
let rotation = (exponent % 61 + 61) % 61
(
@bigint.BigInt::from_uint64(significand) *
(@bigint.BigInt::from_int(1) << rotation),
value < 0.0,
)
}
}
let magnitude = (magnitude % modulus).to_int64()
let signed = if negative { -magnitude } else { magnitude }
if signed == -1L {
-2L
} else {
signed
}
}
///|
pub fn EngPrefixKey::new(value : ScalarNumber) -> EngPrefixKey {
{ number: value, numeric_hash: eng_prefix_hash(value), }
}
///|
pub fn EngPrefixKey::value(self : EngPrefixKey) -> ScalarNumber {
self.number
}
///|
fn eng_prefix_compare(
left : ScalarNumber,
right : ScalarNumber,
) -> Int32? raise @core.DataError {
// NumPy's bool scalar consumes a Python integer through C long, even for
// comparisons. NumPy int64 instead keeps arbitrary Python integers exact.
match (left, right) {
(NumpyBool(_), Integer(_)) => ignore(scalar_numeric_int64(right))
(Integer(_), NumpyBool(_)) => ignore(scalar_numeric_int64(left))
_ => ()
}
scalar_numeric_compare(left, right)
}
///|
fn eng_prefix_less(
left : ScalarNumber,
right : ScalarNumber,
) -> Bool raise @core.DataError {
eng_prefix_compare(left, right) == Some(-1)
}
///|
priv struct EngPrefixEntry {
key : EngPrefixKey
mut value : String
}
///|
/// An ordered numeric dictionary with CPython's combined-table probing.
pub struct EngPrefixes {
priv mut entries : Array[EngPrefixEntry?]
priv mut indices : Array[Int32]
priv mut used : Int32
priv mut usable : Int32
}
///|
pub fn EngPrefixes::new() -> EngPrefixes {
{ entries: [], indices: [], used: 0, usable: 0, }
}
///|
fn eng_prefix_table_size(minimum : Int32) -> Int32 {
let mut size = 8
while size < minimum {
size *= 2
}
size
}
///|
fn eng_prefix_empty_slot(store : EngPrefixes, hash : Int64) -> Int32 {
let mask = (store.indices.length() - 1).to_uint64()
let mut perturb = hash.reinterpret_as_uint64()
let mut slot = perturb & mask
while store.indices[slot.to_int()] >= 0 {
perturb = perturb >> 5
slot = (slot * 5UL + perturb + 1UL) & mask
}
slot.to_int()
}
///|
fn eng_prefix_resize(store : EngPrefixes, size : Int32) -> Unit {
let entries : Array[EngPrefixEntry?] = []
for entry in store.entries {
if entry is Some(_) {
entries.push(entry)
}
}
store.entries = entries
store.indices = Array::make(size, -1)
store.usable = size * 2 / 3 - store.used
for index, item in entries {
if item is Some(entry) {
store.indices[eng_prefix_empty_slot(store, entry.key.numeric_hash)] = index
}
}
}
///|
fn eng_prefix_insert_absent(
store : EngPrefixes,
key : EngPrefixKey,
value : String,
) -> Unit {
if store.indices.is_empty() {
eng_prefix_resize(store, 8)
} else if store.usable == 0 {
eng_prefix_resize(store, eng_prefix_table_size(store.used * 3))
}
let slot = eng_prefix_empty_slot(store, key.numeric_hash)
store.indices[slot] = store.entries.length()
store.entries.push(Some({ key, value, }))
store.used += 1
store.usable -= 1
}
///|
pub fn EngPrefixes::si() -> EngPrefixes {
let store = EngPrefixes::new()
for
pair in [
(-30, "q"),
(-27, "r"),
(-24, "y"),
(-21, "z"),
(-18, "a"),
(-15, "f"),
(-12, "p"),
(-9, "n"),
(-6, "ยต"),
(-3, "m"),
(0, ""),
(3, "k"),
(6, "M"),
(9, "G"),
(12, "T"),
(15, "P"),
(18, "E"),
(21, "Z"),
(24, "Y"),
(27, "R"),
(30, "Q"),
] {
eng_prefix_insert_absent(
store,
EngPrefixKey::new(ScalarNumber::Integer(@bigint.BigInt::from_int(pair.0))),
pair.1,
)
}
store
}
///|
pub fn EngPrefixes::length(self : EngPrefixes) -> Int32 {
self.used
}
///|
pub fn EngPrefixes::keys(self : EngPrefixes) -> Array[EngPrefixKey] {
let keys : Array[EngPrefixKey] = []
for entry in self.entries {
if entry is Some(entry) {
keys.push(entry.key)
}
}
keys
}
///|
pub fn EngPrefixes::copy(self : EngPrefixes) -> EngPrefixes {
if self.used == 0 {
return EngPrefixes::new()
}
// PyDict_Copy retains its physical indices while no more than one third of
// the ordered entries have become tombstones; otherwise it compacts.
if self.used >= self.entries.length() * 2 / 3 {
return {
entries: self.entries.map(item => {
item.map(entry => { key: entry.key, value: entry.value, })
}),
indices: self.indices.copy(),
used: self.used,
usable: self.usable,
}
}
let store = EngPrefixes::new()
eng_prefix_resize(store, eng_prefix_table_size((self.used * 3 + 1) / 2))
for item in self.entries {
if item is Some(entry) {
eng_prefix_insert_absent(store, entry.key, entry.value)
}
}
store
}
///|
fn eng_prefix_find(
store : EngPrefixes,
key : EngPrefixKey,
) -> (Int32, Int32)? raise @core.DataError {
if store.indices.is_empty() {
return None
}
let mask = (store.indices.length() - 1).to_uint64()
let mut perturb = key.numeric_hash.reinterpret_as_uint64()
let mut slot = perturb & mask
while true {
let index = store.indices[slot.to_int()]
if index == -1 {
return None
}
if index >= 0 {
let entry = store.entries[index].unwrap()
if physical_equal(entry.key, key) ||
(
entry.key.numeric_hash == key.numeric_hash &&
eng_prefix_compare(entry.key.number, key.number) == Some(0)
) {
return Some((index, slot.to_int()))
}
}
perturb = perturb >> 5
slot = (slot * 5UL + perturb + 1UL) & mask
} nobreak {
None
}
}
///|
pub fn EngPrefixes::set_key(
self : EngPrefixes,
key : EngPrefixKey,
value : String,
) -> Unit raise @core.DataError {
match eng_prefix_find(self, key) {
Some((index, _)) => self.entries[index].unwrap().value = value
None => eng_prefix_insert_absent(self, key, value)
}
}
///|
pub fn EngPrefixes::get_key(
self : EngPrefixes,
key : EngPrefixKey,
) -> String? raise @core.DataError {
eng_prefix_find(self, key).map(found => self.entries[found.0].unwrap().value)
}
///|
pub fn EngPrefixes::remove_key(
self : EngPrefixes,
key : EngPrefixKey,
) -> Bool raise @core.DataError {
match eng_prefix_find(self, key) {
Some((index, slot)) => {
self.indices[slot] = -2
self.entries[index] = None
self.used -= 1
true
}
None => false
}
}
///|
pub fn EngPrefixes::set(
self : EngPrefixes,
key : ScalarNumber,
value : String,
) -> Unit raise @core.DataError {
self.set_key(EngPrefixKey::new(key), value)
}
///|
pub fn EngPrefixes::get(
self : EngPrefixes,
key : ScalarNumber,
) -> String? raise @core.DataError {
self.get_key(EngPrefixKey::new(key))
}
///|
pub fn EngPrefixes::remove(
self : EngPrefixes,
key : ScalarNumber,
) -> Bool raise @core.DataError {
self.remove_key(EngPrefixKey::new(key))
}
///|
fn eng_places_format(places : ScalarNumber?) -> String raise @core.DataError {
let places = match places {
None => return "g"
Some(Integer(value)) => scalar_percent_integer_str(value)
Some(NumpyInt64(value)) => value.to_string()
Some(Boolean(value) | NumpyBool(value)) => if value { "1" } else { "0" }
Some(Number(_) | NumpyFloat64(_)) =>
raise @core.DataError::InvalidTicks(
message="ValueError: Unknown format code 'd' for object of type 'float'",
)
}
"." + places + "f"
}