Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions mathics/core/atoms/strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import sympy

from mathics.core.element import BoxElementMixin
from mathics.core.keycomparable import BASIC_ATOM_STRING_ELT_ORDER
from mathics.core.keycomparable import BASIC_ATOM_STRING_ELT_ORDER, wma_str_sort_key
from mathics.core.symbols import Atom, Symbol, SymbolFalse, SymbolTrue, symbol_set
from mathics.core.systemsymbols import SymbolFullForm, SymbolInputForm

Expand Down Expand Up @@ -69,7 +69,7 @@ def element_order(self) -> tuple:
"""
return (
BASIC_ATOM_STRING_ELT_ORDER,
self.value,
wma_str_sort_key(self.value),
0,
1,
)
Expand Down
7 changes: 6 additions & 1 deletion mathics/core/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
GENERAL_EXPRESSION_ELT_ORDER,
GENERAL_NUMERIC_EXPRESSION_ELT_ORDER,
Monomial,
wma_str_sort_key,
)
from mathics.core.structure import LinkedStructure
from mathics.core.symbols import (
Expand Down Expand Up @@ -893,8 +894,9 @@ def element_order(self) -> tuple:
3: tuple: list of Elements
4: 1: No clue...
"""
exps: Dict[str, Union[float, complex]] = {}
exps: Dict[Tuple[str, str], Union[float, complex]] = {}
head = self._head

if head is SymbolTimes:
for element in self.elements:
name = element.get_name()
Expand All @@ -904,8 +906,10 @@ def element_order(self) -> tuple:
assert isinstance(expr, (Expression, NumericOperators))
exp = expr.round_to_float()
if var and exp is not None:
var = wma_str_sort_key(var)
exps[var] = exps.get(var, 0) + exp
elif name:
name = wma_str_sort_key(name)
exps[name] = exps.get(name, 0) + 1
elif self.has_form("Power", 2):
var = self.elements[0].get_name()
Expand All @@ -917,6 +921,7 @@ def element_order(self) -> tuple:
except AttributeError:
exp = None
if var and exp is not None:
var = wma_str_sort_key(var)
exps[var] = exps.get(var, 0) + exp
if exps:
return (
Expand Down
36 changes: 36 additions & 0 deletions mathics/core/keycomparable.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Base classes for canonical order.

"""
from typing import Tuple


class KeyComparable:
Expand Down Expand Up @@ -287,3 +288,38 @@ def __ne__(self, other) -> bool:

BASIC_EXPRESSION_ELT_ORDER = 0x22
GENERAL_EXPRESSION_ELT_ORDER = 0x23


def wma_str_sort_key(s: str) -> Tuple[str, str]:
"""
Return a Tuple providing the sort key
reproduce the order of strings and symbols
in WMA.
For example, the following is a list of sorted
strings in the WMA order:
`{Abeja, ABEJA, ave de paso, Ave de paso, Ave de Paso, AVe}`
The order criteria is: first sort case insensitive, then
for the first different character in the original string,
lower case comes before upper case.
"""
# An alternative to this implementation would be to map the
# characters in a way that
# a -> A
# A -> B
# b -> C
# B -> D
# ...
# m -> Z
# M -> a
# n -> b
# N -> c
# ...
# z -> y
# Z -> z
# so the result is again a string. Another possibility would be
# to return a wrapper class that implement this special comparison
# on the fly through the method `__lt__`.
return (
s.lower(),
s.swapcase(),
)
7 changes: 5 additions & 2 deletions mathics/core/symbols.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
BASIC_EXPRESSION_ELT_ORDER,
BASIC_NUMERIC_EXPRESSION_ELT_ORDER,
Monomial,
wma_str_sort_key,
)
from mathics.eval.tracing import trace_evaluate

Expand Down Expand Up @@ -556,15 +557,17 @@ def element_order(self) -> tuple:
Return a tuple value that is used in ordering elements
of an expression. The tuple is ultimately compared lexicographically.
"""
name = self.name
name_key = wma_str_sort_key(name)
return (
(
BASIC_NUMERIC_EXPRESSION_ELT_ORDER
if self.is_numeric()
else BASIC_EXPRESSION_ELT_ORDER
),
Monomial({self.name: 1}),
Monomial({name_key: 1}),
0,
self.name,
name,
1,
)

Expand Down
39 changes: 39 additions & 0 deletions test/builtin/test_sort.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,49 @@
# -*- coding: utf-8 -*-

from test.helper import check_evaluation

from mathics.core.expression import Expression
from mathics.core.symbols import Symbol, SymbolPlus, SymbolTimes


def test_sort_wma():
"""Test the alphabetic order in WMA for Strings and Symbols"""
# In Python, str are ordered as tuples of
# ascii codes of the characters. So,
#
# "Abeja" <"Ave"<"aVe"<"abeja"
#
# In WMA, strings and symbols are sorted in alphabetical order, with
# lowercaps characters coming before than the corresponding upper case.
# Then, the same words are sorted in WMA as
#
# "abeja"< "Abeja"<"aVe"<"Ave"
#
# Such order is equivalent to use
# `lambda s: (s.lower(), s.swapcaps(),)` as sort key.
#
# Finally, String atoms comes before than Symbols. The following test
# reinforce this order.
str_expr = (
'{"Ave", "aVe", "abeja", AVe, ave, aVe, "Abeja", "ABEJA", '
'"AVe", "ave del paraíso", "Ave del paraíso", '
'"Ave del Paraíso"} // Sort // InputForm'
)
str_expected = (
'{"abeja", "Abeja", "ABEJA", "aVe", "Ave", "AVe", '
'"ave del paraíso", "Ave del paraíso", "Ave del Paraíso", '
"ave, aVe, AVe}//InputForm"
)
check_evaluation(
str_expr,
str_expected,
# to_string_expr=True,
# to_string_expected=True,
# hold_expected=True,
failure_message="WMA order",
)


def test_Expression_sameQ():
"""
Test Expression.SameQ
Expand Down
Loading