diff --git a/main/SS/Formula/Eval/FunctionEval.cs b/main/SS/Formula/Eval/FunctionEval.cs
index abcc9607d..6c79df50b 100644
--- a/main/SS/Formula/Eval/FunctionEval.cs
+++ b/main/SS/Formula/Eval/FunctionEval.cs
@@ -334,7 +334,7 @@ private static Function[] ProduceFunctions()
retval[247] = new NotImplementedFunction("DB"); // DB
retval[248] = new NotImplementedFunction("PAUSE"); // PAUSE
retval[250] = new NotImplementedFunction("RESUME"); // RESUME
- retval[252] = new NotImplementedFunction("FREQUENCY"); // FREQUENCY
+ retval[252] = Frequency.Instance; // FREQUENCY
retval[253] = new NotImplementedFunction("AddTOOLBAR"); // AddTOOLBAR
retval[254] = new NotImplementedFunction("DELETETOOLBAR"); // DELETETOOLBAR
retval[FunctionID.EXTERNAL_FUNC] = null; // ExternalFunction is a FreeREfFunction
diff --git a/main/SS/Formula/Functions/Frequency.cs b/main/SS/Formula/Functions/Frequency.cs
new file mode 100644
index 000000000..89bcf7210
--- /dev/null
+++ b/main/SS/Formula/Functions/Frequency.cs
@@ -0,0 +1,104 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace NPOI.SS.Formula.Functions
+{
+ using NPOI.SS.Formula;
+ using NPOI.SS.Formula.Eval;
+ using NPOI.Util;
+ using NPOI.Util.ArrayExtensions;
+
+
+ ///
+ ///
+ /// Implementation of Excel 'Analysis ToolPak' function FREQUENCY()
+ /// Returns a frequency distribution as a vertical array
+ ///
+ ///
+ /// Syntax
+ /// FREQUENCY(data_array, bins_array)
+ ///
+ ///
+ /// data_array Required. An array of or reference to a Set of values for which you want to count frequencies.
+ /// If data_array contains no values, FREQUENCY returns an array of zeros.
+ /// bins_array Required. An array of or reference to intervals into which you want to group the values in data_array.
+ /// If bins_array contains no values, FREQUENCY returns the number of elements in data_array.
+ ///
+ ///
+ public class Frequency : Fixed2ArgFunction
+ {
+ public static Function Instance = new Frequency();
+
+ private Frequency()
+ {
+ // enforce singleton
+ }
+ public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1)
+ {
+ MatrixFunction.MutableValueCollector collector = new MatrixFunction.MutableValueCollector(false, false);
+
+ double[] values;
+ double[] bins;
+ try
+ {
+ values = collector.collectValues(arg0);
+ bins = collector.collectValues(arg1);
+ }
+ catch(EvaluationException e)
+ {
+ return e.GetErrorEval();
+ }
+
+ // can bins be not sorted?
+ //bins = Arrays.stream(bins).sorted().distinct().ToArray();
+
+ int[] histogram = Histogram(values, bins);
+
+ NumberEval[] result = new NumberEval[histogram.Length]; // Arrays.Stream(histogram).boxed().map(NumberEval::new).ToArray(NumberEval[]::new);
+ for(var i = 0; i= 0 ? idx + 1 : -idx;
+ }
+
+ public static int[] Histogram(double[] values, double[] bins)
+ {
+ int[] histogram = new int[bins.Length + 1];
+ foreach(double val in values)
+ {
+ histogram[FindBin(val, bins) - 1]++;
+ }
+ return histogram;
+ }
+ }
+}
+
diff --git a/testcases/main/SS/Formula/Functions/TestFrequency.cs b/testcases/main/SS/Formula/Functions/TestFrequency.cs
new file mode 100644
index 000000000..b78131d96
--- /dev/null
+++ b/testcases/main/SS/Formula/Functions/TestFrequency.cs
@@ -0,0 +1,108 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace NPOI.SS.Formula.Functions
+{
+ using NPOI.HSSF.UserModel;
+ using NPOI.SS.UserModel;
+ using NPOI.SS.Util;
+ using NPOI.Util;
+ using NPOI.Util.ArrayExtensions;
+ using NUnit.Framework;
+ using static NPOI.SS.Formula.Functions.Frequency;
+
+ ///
+ /// Testcase for the function FREQUENCY(data, bins)
+ ///
+ ///
+ /// @author Yegor Kozlov
+ ///
+ [TestFixture]
+ public class TestFrequency
+ {
+
+ [Test]
+ public void TestHistogram()
+ {
+
+ Assert.IsTrue(Arrays.Equals(new int[] { 3, 2, 2, 0, 1, 1 },
+ Histogram(
+ new double[] { 11, 12, 13, 21, 29, 36, 40, 58, 69 },
+ new double[] { 20, 30, 40, 50, 60 })
+ ));
+
+ Assert.IsTrue(Arrays.Equals(new int[] { 1, 1, 1, 1, 1, 0 },
+ Histogram(
+ new double[] { 20, 30, 40, 50, 60 },
+ new double[] { 20, 30, 40, 50, 60 })
+
+ ));
+
+ Assert.IsTrue(Arrays.Equals(new int[] { 2, 3 },
+ Histogram(
+ new double[] { 20, 30, 40, 50, 60 },
+ new double[] { 30 })
+
+ ));
+ }
+
+ [Test]
+ public void TestEvaluate()
+ {
+ IWorkbook wb = new HSSFWorkbook();
+ IFormulaEvaluator evaluator = wb.GetCreationHelper().CreateFormulaEvaluator();
+
+ int[] data = {1, 1, 2, 3, 4, 4, 5, 7, 8, 9, 9, 11, 3, 5, 8};
+ int[] bins = {3, 6, 9};
+ ISheet sheet = wb.CreateSheet();
+ IRow dataRow = sheet.CreateRow(0); // A1:O1
+ for(int i = 0; i < data.Length; i++)
+ {
+ dataRow.CreateCell(i).SetCellValue(data[i]);
+ }
+ IRow binsRow = sheet.CreateRow(1);
+ for(int i = 0; i < bins.Length; i++)
+ {
+ // A2:C2
+ binsRow.CreateCell(i).SetCellValue(bins[i]);
+ }
+ IRow fmlaRow = sheet.CreateRow(2);
+ ICellRange arrayFmla = sheet.SetArrayFormula("FREQUENCY(A1:O1,A2:C2)", CellRangeAddress.ValueOf("A3:A6"));
+ ICell b3 = fmlaRow.CreateCell(1); // B3
+ b3.SetCellFormula("COUNT(FREQUENCY(A1:O1,A2:C2))"); // frequency returns a vertical array of bins+1
+
+ ICell c3 = fmlaRow.CreateCell(2);
+ c3.SetCellFormula("SUM(FREQUENCY(A1:O1,A2:C2))"); // sum of the frequency bins should add up to the number of data values
+
+ Assert.AreEqual(5, (int) evaluator.Evaluate(arrayFmla.FlattenedCells[0]).NumberValue);
+ Assert.AreEqual(4, (int) evaluator.Evaluate(arrayFmla.FlattenedCells[1]).NumberValue);
+ Assert.AreEqual(5, (int) evaluator.Evaluate(arrayFmla.FlattenedCells[2]).NumberValue);
+ Assert.AreEqual(1, (int) evaluator.Evaluate(arrayFmla.FlattenedCells[3]).NumberValue);
+
+ Assert.AreEqual(4, (int) evaluator.Evaluate(b3).NumberValue);
+ Assert.AreEqual(15, (int) evaluator.Evaluate(c3).NumberValue);
+
+ }
+ }
+}
+