On Sat, Jul 04, 2026 at 06:19:16PM +0800, Qian Yun wrote:
> Very likely this patch needs further revision.
>
> But I'm posting it here for discussion.
>
> Notes on the patch:
>
> 1. Current order in Localize does not make sense.
> So I removed the export and implementation.
AFAICS you only removed implementation. You should also remove
the export.
> 2. I add the implementation to LocalAlgebra instead.
That looks resonable. I wonder if we should insist on having
positive denominators. We can ensure this when ring is an
ordered ring, that would save comparison of denominator with 0.
> 3. For "<" in QuotientFieldCategory, the implementation
> depends on Rep, whether the Rep is normalized.
>
> 4. For "smaller?" in QuotientFieldCategory, if "<"
> exists, use that instead. This is needed, otherwise
> a test in ncalg.input fails. I suspect somewhere
> in the factoring code, it needs OrderedSet but only
> Comparable is provided.
I think it should be done a bit different. 'has' tests at
runtime have costs, so it is more efficient to store result
in a variable and test the variable inside the function.
Or provide conditional implementation.
You removeconditional export of OrderedSet. But I think we should
conditionally export OrderedRing and possibly remove export of
OrderedIntegralDomain. Why? Only few domains have
OrderedIntegralDomain and it really does not give more information
than Join(OrderedRing, IntegralDomain). But Spad compiler will
_not_ infer OrderedIntegralDomain knowing that domain have
OrderedRing and IntegralDomain. So we need rules for propagation
of OrderedRing (and of course IntegralDomain), but
OrderedIntegralDomain is of limited use.
> 5. This changes the ordering in Kernel and EXPR.
> I think there is agreement that this change of ordering
> will not cause unsoundness in computation in EXPR?
>
> To recap:
>
> If R has OrderedSet, then FRAC R has OrderedSet
> if R has OrderedSet, then SMP R has OrderedSet
>
> But Complex INT only has Comparable, not OrderedSet.
To say the truth we should only export Comparable in
cases when S has OrderedSet or fractions are canonical.
Unfortunately, it is easy to make Fraction(Complex(Integer))
cononical, but it is problematic to write correct condition
("S is Complex(Integer)" is not allowed in export part).
> So for current implementation, smaller?$(Rep of EXPR INT)
> is a total order, but for EXPR COMPLEX INT, it's not.
>
> This patch changes to lexicographical order.
When using canonical representation lexicographical order
is a linear order. Without canonical representation
order is not well defined.
> 6. Thoughts on OrderedSet and Comparable.
>
> If a domain says it has OrderedSet, it means "natural order
> on given structure". It should further state that
> it is OrderedSemiGroup or OrderedAbelianGroup.
I do not think so. Look at ReverseOrderInteger that I included
in replay to Ralf. The order is natural for purpose of
sorting, but the domain is not OrderedRing.
So OrderedSemiGroup says more than Join(OrderedSet, SemiGroup).
OTOH OrderedIntegralDomain is exactly the same as Join(OrderedRing,
IntegralDomain), so is redundant.
> For Comparable, it could be tricky to define "smaller?".
> For Q[sqrt(-5)], it is INTDOM, but not GCDDOM, and not OrderedSet.
> So no canonical representation.
Actually the solution is the same as for expression: remove
irrationalities from denominator. Our trouble is that
we can write is as SAE(FRAC(INT), ..., ...) or as
FRAC(SAE(INT, ..., ...)). The second variant currently does
not have canonical representation. OTOH
Localize(SAE(INT, ..., ...), INT) could be made canonical,
but we lack apropriate function and conditions.
> The lexicographical order doesn't work strictly, for example
> a/b and c/d where ad=bc but a~=c.
>
> In theory Q[sqrt(-5)] can be totally ordered, using cantor
> diagonal method and crossing duplicates.
>
> But that can not be turned into an algorithm.
>
> I suggest this point of view:
> take a list of N elements of R, making it a set by removing
> duplicates (for example by N(N-1)/2 "=" comparisons).
>
> Then "smaller?" (the lexicographical order) is a total order
> for this list/set.
That looks like well known (and extremaly inefficient) method of
forcing canonical representation.
> diff --git a/src/algebra/fraction.spad b/src/algebra/fraction.spad
> index 499616eb..cee6f9e9 100644
> --- a/src/algebra/fraction.spad
> +++ b/src/algebra/fraction.spad
> @@ -36,11 +36,6 @@ Localize(M : Module R,
> x = y == y.den*x.num = x.den*y.num
> numer x == x.num
> denom x == x.den
> - if M has OrderedAbelianGroup then
> - x < y ==
> --- if y.den::R < 0 then (x, y) := (y, x)
> --- if x.den::R < 0 then (x, y) := (y, x)
> - y.den*x.num < x.den*y.num
> x+y == [y.den*x.num+x.den*y.num, x.den*y.den]
> n*x == [n*x.num, x.den]
> r*x == if r = x.den then [x.num, 1] else [r*x.num, x.den]
> @@ -77,6 +72,11 @@ LocalAlgebra(A : Algebra R,
> 1 == 1$A / 1$R
> x : % * y : % == (numer(x) * numer(y)) / (denom(x) * denom(y))
> characteristic() == characteristic()$A
> + if A has OrderedRing then
> + x < y ==
> + ((denom x)::A > 0) = ((denom y)::A > 0) =>
> + denom(y) * numer(x) < denom(x) * numer(y)
> + denom(y) * numer(x) > denom(x) * numer(y)
>
> )abbrev category QFCAT QuotientFieldCategory
> ++ Author:
> @@ -106,7 +106,6 @@ QuotientFieldCategory(S : IntegralDomain) : Category ==
> if S has RetractableTo Integer then
> RetractableTo Integer
> RetractableTo Fraction Integer
> - if S has OrderedSet then OrderedSet
> if S has Comparable then Comparable
> if S has OrderedIntegralDomain then OrderedIntegralDomain
> if S has RealConstant then RealConstant
> @@ -169,13 +168,11 @@ QuotientFieldCategory(S : IntegralDomain) : Category ==
> convert(x : %) : Float == (convert numer x) / (convert denom x)
> convert(x : %) : DoubleFloat == (convert numer x) / (convert denom x)
>
> - if S has OrderedSet then
> - x : % < y : % ==
> - (numer x * denom y) < (numer y * denom x)
> -
> if S has Comparable then
> smaller?(x : %, y : %) ==
> - smaller?(numer x * denom y, numer y * denom x)
> + % has OrderedSet => x < y
> + denom(x) = denom(y) => smaller?(numer(x), numer(y))
> + smaller?(denom(x), denom(y))
>
> if (S has EuclideanDomain) then
> fractionPart x == x - (wholePart(x)::%)
> diff --git a/src/input/rsimp.input b/src/input/rsimp.input
> index 5a4b2f44..50440eca 100644
> --- a/src/input/rsimp.input
> +++ b/src/input/rsimp.input
> @@ -39,10 +39,10 @@ testEquals("rs3a", "sqrt(3)::eI*(sqrt(6) + 3)/3")
>
> w4 := 28*11^(1/2)*15^(1/2)+497
> -- Need subst, otherwise square root would mangle the argument
> -a7715 := subst(a^(1/2), [a = 77/15])
> +a28 := subst(a^(1/2), [a = 28])
> rs4 := ratDenom(rsimp(sqrt(w4)))
> testEquals("w4 - rs4^2", "0")
> -testEquals("rs4", "(30*sqrt(11)::eI + 11*sqrt(15))*a7715/11")
> +testEquals("rs4", "(2*sqrt(15)::eI + sqrt(11))*a28/2")
>
> w5 := 16 - 2*sqrt(29) + 2*sqrt(55 - 10*sqrt(29));
> rs5 := rsimp(sqrt(w5))
> @@ -90,10 +90,9 @@ testEquals("w12 - rs12^5", "0")
> testEquals("rs12", "(3*sqrt(2) + 2*sqrt(3))*a544/2")
>
> w13 := 7*(2*sqrt(3) - sqrt(11))^6
> -a12096_1331 := subst(a^(1/6), [a = 12096/1331])
> rs13 := rsimp(w13^(1/6))
> testEquals("w13 - rs13^6", "0")
> -testEquals("rs13", "(6*sqrt(11) - 11*sqrt(3))*a12096_1331/6")
> +testEquals("rs13", "7^(1/6)*(sqrt(11)-2*sqrt(3))")
>
> w14 := 19*(5*sqrt(2) - sqrt(7))^7
> rs14 := rsimp(w14^(1/7))
--
Waldek Hebisch