Skip to content

Commit

Permalink
MONDRIAN: Fix bug 1455969, "Sorting broken by NaN or Infinity".
Browse files Browse the repository at this point in the history
[git-p4: depot-paths = "//open/mondrian/": change = 7726]
  • Loading branch information
julianhyde committed Sep 21, 2006
1 parent 6e8f437 commit 885f695
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 5 deletions.
36 changes: 31 additions & 5 deletions src/main/mondrian/olap/fun/FunUtil.java
Expand Up @@ -422,12 +422,38 @@ static int sign(double d) {
: 1;
}

/**
* Compares double-precision values according to MDX semantics.
*
* <p>MDX requires a total order:
* <pre>
* -inf &lt; ... &lt; -1 &lt; ... &lt; 0 &lt; ... &lt; NaN &lt; +inf
* </pre>
* but this is different than Java semantics, specifically with regard
* to {@link Double#NaN}.
*/
static int compareValues(double d1, double d2) {
return (d1 == d2)
? 0
: (d1 < d2)
? -1
: 1;
if (Double.isNaN(d1)) {
if (d2 == Double.POSITIVE_INFINITY) {
return -1;
} else if (Double.isNaN(d2)) {
return 0;
} else {
return 1;
}
} else if (Double.isNaN(d2)) {
if (d1 == Double.POSITIVE_INFINITY) {
return 1;
} else {
return -1;
}
} else if (d1 == d2) {
return 0;
} else if (d1 < d2) {
return -1;
} else {
return 1;
}
}

static int compareValues(int i, int j) {
Expand Down
159 changes: 159 additions & 0 deletions testsrc/main/mondrian/olap/fun/SortTest.java
@@ -0,0 +1,159 @@
/*
// $Id$
// This software is subject to the terms of the Common Public License
// Agreement, available at the following URL:
// http://www.opensource.org/licenses/cpl.html.
// Copyright (C) 2006-2006 Julian Hyde
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
*/
package mondrian.olap.fun;

import mondrian.test.FoodMartTestCase;

/**
* <code>SortTest</code> tests the collation order of positive and negative
* infinity, and {@link Double#NaN}.
*
* @author jhyde
* @version $Id$
* @since Sep 21, 2006
*/
public class SortTest extends FoodMartTestCase
{
public void testFoo() {
// Check that each value compares according to its position in the total
// order. For example, NaN compares greater than
// Double.NEGATIVE_INFINITY, -34.5, -0.001, 0, 0.00000567, 1, 3.14;
// equal to NaN; and less than Double.POSITIVE_INFINITY.
double[] values = {
Double.NEGATIVE_INFINITY,
-34.5,
-0.001,
0,
0.00000567,
1,
3.14,
Double.NaN,
Double.POSITIVE_INFINITY,
};
for (int i = 0; i < values.length; i++) {
for (int j = 0; j < values.length; j++) {
int expected = i < j ? -1 : i == j ? 0 : 1;
assertEquals(
"values[" + i + "]=" + values[i] + ", values[" + j +
"]=" + values[j],
expected,
FunUtil.compareValues(values[i], values[j]));
}
}
}

public void testOrderDesc() {
assertQueryReturns("with member [Measures].[Foo] as '\n" +
" Iif( [Promotion Media].CurrentMember IS [Promotion Media].[TV], 1.0 / 0.0,\n" +
" Iif( [Promotion Media].CurrentMember IS [Promotion Media].[Radio], -1.0 / 0.0,\n" +
" Iif( [Promotion Media].CurrentMember IS [Promotion Media].[Bulk Mail], 0.0 / 0.0,\n" +
" [Measures].[Unit Sales]))) '\n" +
"select \n" +
" {[Measures].[Foo]} on columns, \n" +
" order(except([Promotion Media].[Media Type].members,{[Promotion Media].[Media Type].[No Media]}),[Measures].[Foo],DESC) on rows\n" +
"from Sales",
fold("Axis #0:\n" +
"{}\n" +
"Axis #1:\n" +
"{[Measures].[Foo]}\n" +
"Axis #2:\n" +
"{[Promotion Media].[All Media].[TV]}\n" +
"{[Promotion Media].[All Media].[Bulk Mail]}\n" +
"{[Promotion Media].[All Media].[Daily Paper, Radio, TV]}\n" +
"{[Promotion Media].[All Media].[Daily Paper]}\n" +
"{[Promotion Media].[All Media].[Product Attachment]}\n" +
"{[Promotion Media].[All Media].[Daily Paper, Radio]}\n" +
"{[Promotion Media].[All Media].[Cash Register Handout]}\n" +
"{[Promotion Media].[All Media].[Sunday Paper, Radio]}\n" +
"{[Promotion Media].[All Media].[Street Handout]}\n" +
"{[Promotion Media].[All Media].[Sunday Paper]}\n" +
"{[Promotion Media].[All Media].[In-Store Coupon]}\n" +
"{[Promotion Media].[All Media].[Sunday Paper, Radio, TV]}\n" +
"{[Promotion Media].[All Media].[Radio]}\n" +
"Row #0: Infinity\n" +
"Row #1: NaN\n" +
"Row #2: 9,513\n" +
"Row #3: 7,738\n" +
"Row #4: 7,544\n" +
"Row #5: 6,891\n" +
"Row #6: 6,697\n" +
"Row #7: 5,945\n" +
"Row #8: 5,753\n" +
"Row #9: 4,339\n" +
"Row #10: 3,798\n" +
"Row #11: 2,726\n" +
"Row #12: -Infinity\n"));
}

public void testOrderAndRank() {
assertQueryReturns("with " +
" member [Measures].[Foo] as '\n" +
" Iif( [Promotion Media].CurrentMember IS [Promotion Media].[TV], 1.0 / 0.0,\n" +
" Iif( [Promotion Media].CurrentMember IS [Promotion Media].[Radio], -1.0 / 0.0,\n" +
" Iif( [Promotion Media].CurrentMember IS [Promotion Media].[Bulk Mail], 0.0 / 0.0,\n" +
" [Measures].[Unit Sales]))) '\n" +
" member [Measures].[R] as '\n" +
" Rank([Promotion Media].CurrentMember, [Promotion Media].Members, [Measures].[Foo]) '\n" +
"select\n" +
" {[Measures].[Foo], [Measures].[R]} on columns, \n" +
" order([Promotion Media].[Media Type].members,[Measures].[Foo]) on rows\n" +
"from Sales",
fold("Axis #0:\n" +
"{}\n" +
"Axis #1:\n" +
"{[Measures].[Foo]}\n" +
"{[Measures].[R]}\n" +
"Axis #2:\n" +
"{[Promotion Media].[All Media].[Radio]}\n" +
"{[Promotion Media].[All Media].[Sunday Paper, Radio, TV]}\n" +
"{[Promotion Media].[All Media].[In-Store Coupon]}\n" +
"{[Promotion Media].[All Media].[Sunday Paper]}\n" +
"{[Promotion Media].[All Media].[Street Handout]}\n" +
"{[Promotion Media].[All Media].[Sunday Paper, Radio]}\n" +
"{[Promotion Media].[All Media].[Cash Register Handout]}\n" +
"{[Promotion Media].[All Media].[Daily Paper, Radio]}\n" +
"{[Promotion Media].[All Media].[Product Attachment]}\n" +
"{[Promotion Media].[All Media].[Daily Paper]}\n" +
"{[Promotion Media].[All Media].[Daily Paper, Radio, TV]}\n" +
"{[Promotion Media].[All Media].[No Media]}\n" +
"{[Promotion Media].[All Media].[Bulk Mail]}\n" +
"{[Promotion Media].[All Media].[TV]}\n" +
"Row #0: -Infinity\n" +
"Row #0: 15\n" +
"Row #1: 2,726\n" +
"Row #1: 14\n" +
"Row #2: 3,798\n" +
"Row #2: 13\n" +
"Row #3: 4,339\n" +
"Row #3: 12\n" +
"Row #4: 5,753\n" +
"Row #4: 11\n" +
"Row #5: 5,945\n" +
"Row #5: 10\n" +
"Row #6: 6,697\n" +
"Row #6: 9\n" +
"Row #7: 6,891\n" +
"Row #7: 8\n" +
"Row #8: 7,544\n" +
"Row #8: 7\n" +
"Row #9: 7,738\n" +
"Row #9: 6\n" +
"Row #10: 9,513\n" +
"Row #10: 5\n" +
"Row #11: 195,448\n" +
"Row #11: 4\n" +
"Row #12: NaN\n" +
"Row #12: 2\n" +
"Row #13: Infinity\n" +
"Row #13: 1\n"));
}
}

// End SortTest.java
2 changes: 2 additions & 0 deletions testsrc/main/mondrian/test/Main.java
Expand Up @@ -27,6 +27,7 @@
import mondrian.olap.Util;
import mondrian.olap.UtilTestCase;
import mondrian.olap.fun.FunctionTest;
import mondrian.olap.fun.SortTest;
import mondrian.olap.HierarchyBugTest;
import mondrian.rolap.*;
import mondrian.test.comp.ResultComparatorTest;
Expand Down Expand Up @@ -154,6 +155,7 @@ public static Test suite() throws Exception {
suite.addTestSuite(HierarchyBugTest.class);
suite.addTestSuite(ScheduleTest.class);
suite.addTestSuite(UtilTestCase.class);
suite.addTestSuite(SortTest.class);
if (isRunOnce()) suite.addTestSuite(TestAggregationManager.class);
suite.addTestSuite(VirtualCubeTest.class);
suite.addTestSuite(ParameterTest.class);
Expand Down

0 comments on commit 885f695

Please sign in to comment.