diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/CalcEngine.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/CalcEngine.cs index 22c24db..b0d924a 100644 --- a/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/CalcEngine.cs +++ b/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/CalcEngine.cs @@ -20,7 +20,7 @@ /// Use the RegisterFunction method to define custom functions. /// Override the GetExternalObject method to add arbitrary variables to the engine scope. /// - public partial class CalcEngine + internal class CalcEngine { //--------------------------------------------------------------------------- #region ** fields @@ -776,5 +776,5 @@ /// List of objects that represent the /// parameters to be used in the function call. /// The function result. - public delegate object CalcEngineFunction(List parms); + internal delegate object CalcEngineFunction(List parms); } diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Expression.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Expression.cs index 1b1732e..30f9b7f 100644 --- a/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Expression.cs +++ b/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Expression.cs @@ -19,7 +19,7 @@ /// object val = expr.Evaluate(); /// /// - public class Expression : IComparable + internal class Expression : IComparable { //--------------------------------------------------------------------------- #region ** fields diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/FunctionDefinition.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/FunctionDefinition.cs index 6438ce8..fc02182 100644 --- a/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/FunctionDefinition.cs +++ b/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/FunctionDefinition.cs @@ -7,7 +7,7 @@ /// /// Function definition class (keeps function name, parameter counts, and delegate). /// - public class FunctionDefinition + internal class FunctionDefinition { // ** fields public int ParmMin, ParmMax; diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Functions/Logical.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Functions/Logical.cs index c2cd087..391fed9 100644 --- a/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Functions/Logical.cs +++ b/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Functions/Logical.cs @@ -5,7 +5,7 @@ namespace ClosedXML.Excel.CalcEngine { - static class Logical + internal static class Logical { public static void Register(CalcEngine ce) { diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Functions/MathTrig.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Functions/MathTrig.cs index a0edb8e..84fb2f0 100644 --- a/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Functions/MathTrig.cs +++ b/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Functions/MathTrig.cs @@ -54,10 +54,10 @@ ce.RegisterFunction("RAND", 0, Rand); ce.RegisterFunction("RANDBETWEEN", 2, RandBetween); ce.RegisterFunction("ROMAN", 1, 2, Roman); - //ce.RegisterFunction("ROUND", Round, 1); - //ce.RegisterFunction("ROUNDDOWN", RoundDown, 1); - //ce.RegisterFunction("ROUNDUP", RoundUp, 1); - //ce.RegisterFunction("SERIESSUM", SeriesSum, 1); + ce.RegisterFunction("ROUND", 2, Round); + ce.RegisterFunction("ROUNDDOWN", 2, RoundDown); + ce.RegisterFunction("ROUNDUP", 1, 2, RoundUp); + ce.RegisterFunction("SERIESSUM", 4, SeriesSum); ce.RegisterFunction("SIGN", 1, Sign); ce.RegisterFunction("SIN", 1, Sin); ce.RegisterFunction("SINH", 1, Sinh); @@ -522,11 +522,72 @@ Int32 intTemp; Boolean boolTemp; if (p.Count == 1 - || (Boolean.TryParse(p[1].ToString(), out boolTemp) && boolTemp) - || (Int32.TryParse(p[1].ToString(), out intTemp) && intTemp == 1)) + || (Boolean.TryParse(p[1]._token.Value.ToString(), out boolTemp) && boolTemp) + || (Int32.TryParse(p[1]._token.Value.ToString(), out intTemp) && intTemp == 1)) return XLMath.ToRoman((int)p[0]); throw new ArgumentException("Can only support classic roman types."); } + + private static object Round(List p) + { + var value = (Double)p[0]; + var digits = (Int32)(Double)p[1]; + if (digits >= 0) + { + return Math.Round(value, digits); + } + else + { + digits = Math.Abs(digits); + double temp = value / Math.Pow(10, digits); + temp = Math.Round(temp, 0); + return temp * Math.Pow(10, digits); + } + + } + + private static object RoundDown(List p) + { + var value = (Double)p[0]; + var digits = (Int32)(Double)p[1]; + + if (value >= 0) + return Math.Floor(value * Math.Pow(10, digits)) / Math.Pow(10, digits); + + return Math.Ceiling(value * Math.Pow(10, digits)) / Math.Pow(10, digits); + } + + private static object RoundUp(List p) + { + var value = (Double)p[0]; + var digits = (Int32)(Double)p[1]; + + if (value >= 0) + return Math.Ceiling(value * Math.Pow(10, digits)) / Math.Pow(10, digits); + + return Math.Floor(value * Math.Pow(10, digits)) / Math.Pow(10, digits); + } + + private static object SeriesSum(List p) + { + var x = (Double)p[0]; + var n = (Double)p[1]; + var m = (Double)p[2]; + var obj = p[3] as XObjectExpression; + + if (obj == null) + return p[3] * Math.Pow(x , n); + + Double total = 0; + Int32 i = 0; + foreach (var e in obj) + { + total += (double)e * Math.Pow(x, n + i * m); + i++; + } + + return total; + } } } \ No newline at end of file diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Functions/Statistical.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Functions/Statistical.cs index a6015bb..c0e91cd 100644 --- a/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Functions/Statistical.cs +++ b/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Functions/Statistical.cs @@ -5,7 +5,7 @@ namespace ClosedXML.Excel.CalcEngine { - static class Statistical + internal static class Statistical { public static void Register(CalcEngine ce) { diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Functions/Tally.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Functions/Tally.cs index 74c3afd..989497a 100644 --- a/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Functions/Tally.cs +++ b/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Functions/Tally.cs @@ -4,7 +4,7 @@ namespace ClosedXML.Excel.CalcEngine { - class Tally + internal class Tally { double _sum, _sum2, _cnt, _min, _max; bool _numbersOnly; diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Functions/Text.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Functions/Text.cs index ce80362..dbd7ba4 100644 --- a/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Functions/Text.cs +++ b/ClosedXML/ClosedXML/ClosedXML/Excel/CalcEngine/Functions/Text.cs @@ -6,7 +6,7 @@ namespace ClosedXML.Excel.CalcEngine { - static class Text + internal static class Text { public static void Register(CalcEngine ce) { diff --git a/ClosedXML/ClosedXML/ClosedXML_Tests/Excel/CalcEngine/FunctionsTests.cs b/ClosedXML/ClosedXML/ClosedXML_Tests/Excel/CalcEngine/FunctionsTests.cs index 1e1970e..e0459f6 100644 --- a/ClosedXML/ClosedXML/ClosedXML_Tests/Excel/CalcEngine/FunctionsTests.cs +++ b/ClosedXML/ClosedXML/ClosedXML_Tests/Excel/CalcEngine/FunctionsTests.cs @@ -199,14 +199,101 @@ [TestMethod] public void Roman() { - var actual = XLWorkbook.EvaluateExpr("Roman(3046)"); + var actual = XLWorkbook.EvaluateExpr("Roman(3046, 1)"); Assert.AreEqual("MMMXLVI", actual); actual = XLWorkbook.EvaluateExpr("Roman(270)"); Assert.AreEqual("CCLXX", actual); - actual = XLWorkbook.EvaluateExpr("Roman(3999)"); + actual = XLWorkbook.EvaluateExpr("Roman(3999, true)"); Assert.AreEqual("MMMCMXCIX", actual); } + + [TestMethod] + public void Round() + { + var actual = XLWorkbook.EvaluateExpr("Round(2.15, 1)"); + Assert.AreEqual(2.2, actual); + + actual = XLWorkbook.EvaluateExpr("Round(2.149, 1)"); + Assert.AreEqual(2.1, actual); + + actual = XLWorkbook.EvaluateExpr("Round(-1.475, 2)"); + Assert.AreEqual(-1.48, actual); + + actual = XLWorkbook.EvaluateExpr("Round(21.5, -1)"); + Assert.AreEqual(20.0, actual); + + actual = XLWorkbook.EvaluateExpr("Round(626.3, -3)"); + Assert.AreEqual(1000.0, actual); + + actual = XLWorkbook.EvaluateExpr("Round(1.98, -1)"); + Assert.AreEqual(0.0, actual); + + actual = XLWorkbook.EvaluateExpr("Round(-50.55, -2)"); + Assert.AreEqual(-100.0, actual); + } + + [TestMethod] + public void RoundDown() + { + var actual = XLWorkbook.EvaluateExpr("RoundDown(3.2, 0)"); + Assert.AreEqual(3.0, actual); + + actual = XLWorkbook.EvaluateExpr("RoundDown(76.9, 0)"); + Assert.AreEqual(76.0, actual); + + actual = XLWorkbook.EvaluateExpr("RoundDown(3.14159, 3)"); + Assert.AreEqual(3.141, actual); + + actual = XLWorkbook.EvaluateExpr("RoundDown(-3.14159, 1)"); + Assert.AreEqual(-3.1, actual); + + actual = XLWorkbook.EvaluateExpr("RoundDown(31415.92654, -2)"); + Assert.AreEqual(31400.0, actual); + + actual = XLWorkbook.EvaluateExpr("RoundDown(0, 3)"); + Assert.AreEqual(0.0, actual); + } + + [TestMethod] + public void RoundUp() + { + var actual = XLWorkbook.EvaluateExpr("RoundUp(3.2, 0)"); + Assert.AreEqual(4.0, actual); + + actual = XLWorkbook.EvaluateExpr("RoundUp(76.9, 0)"); + Assert.AreEqual(77.0, actual); + + actual = XLWorkbook.EvaluateExpr("RoundUp(3.14159, 3)"); + Assert.AreEqual(3.142, actual); + + actual = XLWorkbook.EvaluateExpr("RoundUp(-3.14159, 1)"); + Assert.AreEqual(-3.2, actual); + + actual = XLWorkbook.EvaluateExpr("RoundUp(31415.92654, -2)"); + Assert.AreEqual(31500.0, actual); + + actual = XLWorkbook.EvaluateExpr("RoundUp(0, 3)"); + Assert.AreEqual(0.0, actual); + } + + [TestMethod] + public void SeriesSum() + { + var actual = XLWorkbook.EvaluateExpr("SERIESSUM(2,3,4,5)"); + Assert.AreEqual(40.0, actual); + + var wb = new XLWorkbook(); + var ws = wb.AddWorksheet("Sheet1"); + ws.Cell("A2").FormulaA1 = "PI()/4"; + ws.Cell("A3").Value = 1; + ws.Cell("A4").FormulaA1 = "-1/FACT(2)"; + ws.Cell("A5").FormulaA1 = "1/FACT(4)"; + ws.Cell("A6").FormulaA1 = "-1/FACT(6)"; + + actual = ws.Evaluate("SERIESSUM(A2,0,2,A3:A6)"); + Assert.IsTrue(Math.Abs(0.70710321482284566 - (double)actual) < XLHelper.Epsilon); + } } }