<commit_before>'''Physical numbers combine a quantity, a unit, and a possible name
'''

from __future__ import division
from __future__ import absolute_import

import re
from decimal import Decimal
from functools import partial

from hlab.bases import AutoRepr
from hlab.memorize import memorize

from jamenson.runtime import atypes
from jamenson.runtime.atypes import anytype, as_optimized_type, typep, Seq, as_type
from jamenson.runtime.multimethod import defmethod, MultiMethod
from jamenson.runtime.as_string import as_string

from . import algebra as A
from .sigfig import SigFig
from .ratio import Ratio, as_ratio
from .dne import DNEType, dne
from .units import as_unit, BaseUnit, ex_parse_unit, dimensionless

name_type = as_optimized_type((str,unicode,type(None)))
lossless_number_type = as_optimized_type((int,long,Ratio,Decimal,SigFig,DNEType))


class PhysNum(A.DivAlgebraBase, AutoRepr):

    def __init__(self, quantity, unit=None, name=None):
        assert typep(quantity, lossless_number_type), \
               'bad quantity %r(%s)' % (quantity, type(quantity).__name__)
        self.quantity = quantity
        self.unit = as_unit(unit)
        assert typep(name, name_type)
        self.name = name

    def repr_args(self):
        yield self.quantity
        if self.unit != dimensionless:
            yield self.unit
        if self.name is not None:
            if self.unit == dimensionless:
                yield None
            yield self.name

    def __str__(self):
        parts = [self.quantity]
        if self.unit != dimensionless:
            parts.append(self.unit)
        if self.name is not None:
            parts.append(self.name)
        return ' '.join(map(str, parts))

    def __hash__(self):
        if self.unit==dimensionless and not self.name:
            return hash(self.quantity)
        return hash(self.quantity) ^ hash(self.unit) ^ hash(self.name) ^ 893409342

    def __eq__(self, other):
        if isinstance(other, PhysNum):
            return (self.quantity == other.quantity and
                    self.unit == other.unit and
                    self.name == other.name)
        if self.unit != dimensionless or self.name is not None:
            return NotImplemented
        return self.quantity == other

    def split_posneg(self):
        unit = self.unit
        if isinstance(unit, PrimitiveUnit):
            unit = primunit_to_compound(unit)
        u_pos,u_neg = unit.split_posneg()
        if isinstance(self.quantity, Ratio):
            q_pos,q_neg = self.quantity.num, self.quantity.den
        else:
            q_pos,q_neg = self.quantity, 1
        return [self.__class__(q_pos, u_pos, self.name),
                self.__class__(q_neg, u_neg)]


as_physnum = MultiMethod('as_physnum')

@defmethod(as_physnum, [PhysNum])
def meth(pn):
    return pn

@defmethod(as_physnum, [lossless_number_type])
def meth(n):
    return PhysNum(n)

@defmethod(as_physnum, [str])
def meth(s):
    return parse_physical_number(s)

@defmethod(as_physnum, [unicode])
def meth(u):
    return as_physnum(str(u))

@memorize
def parse_physical_number(bytes, quantity_class=None, create_unit=False):
    parts = bytes.strip().split(None, 1)
    number, rest = (parts if len(parts)==2 else (parts[0], ''))
    unit,extra = ex_parse_unit(rest, create=create_unit) if rest else (dimensionless, None)
    name = extra and extra.strip()
    if number.endswith('d'):
        number = number[:-1]
        quantity_class = quantity_class or Decimal
    if number.endswith('s'):
        number = number[:-1]
        quantity_class = quantity_class or SigFig
    if quantity_class is None:
        quantity_class = int if not re.search('[.eE]', number) else Decimal
    return PhysNum(quantity_class(number), unit, name)

@defmethod(A.mm_neg, [PhysNum])
def meth(p):
    return PhysNum(-p.quantity, p.unit)

@defmethod(A.mm_pow, [PhysNum, (int,long)])
def meth(p,pow):<commit_msg>Replace __eq__ method with mm_eq multimethods using algebra framework<commit_after>'''Physical numbers combine a quantity, a unit, and a possible name
'''

from __future__ import division
from __future__ import absolute_import

import re
from decimal import Decimal
from functools import partial

from hlab.bases import AutoRepr
from hlab.memorize import memorize

from jamenson.runtime import atypes
from jamenson.runtime.atypes import anytype, as_optimized_type, typep, Seq, as_type
from jamenson.runtime.multimethod import defmethod, MultiMethod
from jamenson.runtime.as_string import as_string

from . import algebra as A
from .sigfig import SigFig
from .ratio import Ratio, as_ratio
from .dne import DNEType, dne
from .units import as_unit, BaseUnit, ex_parse_unit, dimensionless

name_type = as_optimized_type((str,unicode,type(None)))
lossless_number_type = as_optimized_type((int,long,Ratio,Decimal,SigFig,DNEType))


class PhysNum(A.DivAlgebraBase, AutoRepr):

    def __init__(self, quantity, unit=None, name=None):
        assert typep(quantity, lossless_number_type), \
               'bad quantity %r(%s)' % (quantity, type(quantity).__name__)
        self.quantity = quantity
        self.unit = as_unit(unit)
        assert typep(name, name_type)
        self.name = name

    def repr_args(self):
        yield self.quantity
        if self.unit != dimensionless:
            yield self.unit
        if self.name is not None:
            if self.unit == dimensionless:
                yield None
            yield self.name

    def __str__(self):
        parts = [self.quantity]
        if self.unit != dimensionless:
            parts.append(self.unit)
        if self.name is not None:
            parts.append(self.name)
        return ' '.join(map(str, parts))

    def __hash__(self):
        if self.unit==dimensionless and not self.name:
            return hash(self.quantity)
        return hash(self.quantity) ^ hash(self.unit) ^ hash(self.name) ^ 893409342

    def split_posneg(self):
        unit = self.unit
        if isinstance(unit, PrimitiveUnit):
            unit = primunit_to_compound(unit)
        u_pos,u_neg = unit.split_posneg()
        if isinstance(self.quantity, Ratio):
            q_pos,q_neg = self.quantity.num, self.quantity.den
        else:
            q_pos,q_neg = self.quantity, 1
        return [self.__class__(q_pos, u_pos, self.name),
                self.__class__(q_neg, u_neg)]


as_physnum = MultiMethod('as_physnum')

@defmethod(as_physnum, [PhysNum])
def meth(pn):
    return pn

@defmethod(as_physnum, [lossless_number_type])
def meth(n):
    return PhysNum(n)

@defmethod(as_physnum, [str])
def meth(s):
    return parse_physical_number(s)

@defmethod(as_physnum, [unicode])
def meth(u):
    return as_physnum(str(u))

@memorize
def parse_physical_number(bytes, quantity_class=None, create_unit=False):
    parts = bytes.strip().split(None, 1)
    number, rest = (parts if len(parts)==2 else (parts[0], ''))
    unit,extra = ex_parse_unit(rest, create=create_unit) if rest else (dimensionless, None)
    name = extra and extra.strip()
    if number.endswith('d'):
        number = number[:-1]
        quantity_class = quantity_class or Decimal
    if number.endswith('s'):
        number = number[:-1]
        quantity_class = quantity_class or SigFig
    if quantity_class is None:
        quantity_class = int if not re.search('[.eE]', number) else Decimal
    return PhysNum(quantity_class(number), unit, name)

@defmethod(A.mm_eq, [PhysNum, PhysNum])
def meth(a, b):
    return (self.quantity == other.quantity and
            self.unit == other.unit and
            self.name == other.name)

@A.defboth_mm_eq([PhysNum, lossless_number_type])
def meth(p, o):
    if p.unit != dimensionless or p.name is not None:
        return NotImplemented
    return p.quantity == o

@defmethod(A.mm_neg, [PhysNum])
def meth(p):
    return PhysNum(-p.quantity, p.unit)

@defmethod(A.mm_pow, [PhysNum, (int,long)])
def meth(p,pow):
{'fraction_non_alphanumeric': 0.104551201011378, 'fraction_numerical': 0.0037926675094816687, 'mean_word_length': 3.144054478784704, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2161979', 'n_tokens_mistral': 2558, 'n_tokens_neox': 2453, 'n_words': 646}