feat(formula): add asin function (#2620)

* feat(formula): add asin function

* feat(formula): add asinh function

* feat(formula): add tan,tanh functions

* test(formula): acot test

* fix(formula): function atan2 calculation

* fix(sheet): strip every number cell

* fix(sheet): remove unused module

* fix(sheet): skip numfmt pattern in number cell interceptor
This commit is contained in:
Dushusir
2024-07-05 11:11:07 +08:00
committed by 白熱
parent bd5293c6fd
commit 88bde30526
32 changed files with 1361 additions and 212 deletions
@@ -1022,6 +1022,11 @@ export class NumberValueObject extends BaseValueObject {
return ErrorValueObject.create(ErrorType.NUM);
}
// ATAN2(y, x) calculates the angle of the point (x, y) relative to the positive x-axis. When x and y are both 0, the point is actually the origin itself, and there is no concept of the angle "starting from" the origin to the origin.
if (currentValue === 0 && value === 0) {
return ErrorValueObject.create(ErrorType.DIV_BY_ZERO);
}
const result = Math.atan2(currentValue, value);
if (!Number.isFinite(result)) {
@@ -18,42 +18,64 @@ import { describe, expect, it } from 'vitest';
import { FUNCTION_NAMES_MATH } from '../../function-names';
import { Abs } from '../index';
import { NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object';
import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object';
import { ArrayValueObject, transformToValue, transformToValueObject } from '../../../../engine/value-object/array-value-object';
import { ErrorType } from '../../../../basics/error-type';
import { ErrorValueObject } from '../../../../engine/value-object/base-value-object';
describe('Test abs function', () => {
const testFunction = new Abs(FUNCTION_NAMES_MATH.ABS);
describe('Abs', () => {
it('Value is normal', () => {
const value = NumberValueObject.create(1);
it('Value is normal number', () => {
const value = NumberValueObject.create(-1);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(1);
});
it('Value is string number', () => {
const value = new StringValueObject('1');
it('Value is number string', () => {
const value = StringValueObject.create('-0.5');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(1);
expect(result.getValue()).toBe(0.5);
});
it('Value is normal string', () => {
const value = StringValueObject.create('test');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.VALUE);
});
it('Value is boolean', () => {
const value = BooleanValueObject.create(false);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0);
});
it('Value is blank cell', () => {
const value = NullValueObject.create();
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0);
});
it('Value is error', () => {
const value = ErrorValueObject.create(ErrorType.NAME);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.NAME);
});
it('Value is array', () => {
const valueArray = ArrayValueObject.create({
calculateValueList: transformToValueObject([
[1, 2],
[2, 3],
[3, 4],
[1, ' ', 1.23, true, false, null],
[0, '100', '2.34', 'test', -3, ErrorType.NAME],
]),
rowCount: 3,
columnCount: 2,
rowCount: 2,
columnCount: 6,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
const result = testFunction.calculate(valueArray);
expect(transformToValue(result.getArrayValue())).toStrictEqual([[1, 2],
[2, 3],
[3, 4]]);
expect(transformToValue(result.getArrayValue())).toStrictEqual([[1, ErrorType.VALUE, 1.23, 1, 0, 0], [0, 100, 2.34, ErrorType.VALUE, 3, ErrorType.NAME]]);
});
});
});
@@ -14,9 +14,7 @@
* limitations under the License.
*/
import { ErrorType } from '../../../basics/error-type';
import type { BaseValueObject } from '../../../engine/value-object/base-value-object';
import { ErrorValueObject } from '../../../engine/value-object/base-value-object';
import { BaseFunction } from '../../base-function';
export class Abs extends BaseFunction {
@@ -30,7 +28,7 @@ export class Abs extends BaseFunction {
}
if (variant.isError()) {
return ErrorValueObject.create(ErrorType.VALUE);
return variant;
}
return variant.abs();
@@ -18,41 +18,70 @@ import { describe, expect, it } from 'vitest';
import { FUNCTION_NAMES_MATH } from '../../function-names';
import { Acos } from '../index';
import { NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object';
import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object';
import { ArrayValueObject, transformToValue, transformToValueObject } from '../../../../engine/value-object/array-value-object';
import { ErrorType } from '../../../../basics/error-type';
import { ErrorValueObject } from '../../../../engine/value-object/base-value-object';
describe('Test acos function', () => {
const testFunction = new Acos(FUNCTION_NAMES_MATH.ACOS);
describe('Acos', () => {
it('Value is normal', () => {
it('Value is normal number', () => {
const value = NumberValueObject.create(1);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0);
});
it('Value is string number', () => {
const value = new StringValueObject('1');
it('Value is number valid', () => {
const value = NumberValueObject.create(-2);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0);
expect(result.getValue()).toBe(ErrorType.NUM);
});
it('Value is number string', () => {
const value = StringValueObject.create('0.5');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(1.0471975511965979);
});
it('Value is normal string', () => {
const value = StringValueObject.create('test');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.VALUE);
});
it('Value is boolean', () => {
const value = BooleanValueObject.create(false);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(1.5707963267948966);
});
it('Value is blank cell', () => {
const value = NullValueObject.create();
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(1.5707963267948966);
});
it('Value is error', () => {
const value = ErrorValueObject.create(ErrorType.NAME);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.NAME);
});
it('Value is array', () => {
const valueArray = ArrayValueObject.create({
calculateValueList: transformToValueObject([
[1, ' ', 1.23, true, false],
[0, '100', '2.34', 'test', -3],
[1, ' ', 1.23, true, false, null],
[0, '100', '2.34', 'test', -3, ErrorType.NAME],
]),
rowCount: 2,
columnCount: 5,
columnCount: 6,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
const result = testFunction.calculate(valueArray);
expect(transformToValue(result.getArrayValue())).toStrictEqual([[0, '#VALUE!', '#NUM!', 0, 1.5707963267948966],
[1.5707963267948966, '#NUM!', '#NUM!', '#VALUE!', '#NUM!']]);
expect(transformToValue(result.getArrayValue())).toStrictEqual([[0, ErrorType.VALUE, ErrorType.NUM, 0, 1.5707963267948966, 1.5707963267948966], [1.5707963267948966, ErrorType.NUM, ErrorType.NUM, ErrorType.VALUE, ErrorType.NUM, ErrorType.NAME]]);
});
});
});
@@ -14,9 +14,7 @@
* limitations under the License.
*/
import { ErrorType } from '../../../basics/error-type';
import type { BaseValueObject } from '../../../engine/value-object/base-value-object';
import { ErrorValueObject } from '../../../engine/value-object/base-value-object';
import { BaseFunction } from '../../base-function';
export class Acos extends BaseFunction {
@@ -30,7 +28,7 @@ export class Acos extends BaseFunction {
}
if (variant.isError()) {
return new ErrorValueObject(ErrorType.VALUE);
return variant;
}
return variant.acos();
@@ -18,41 +18,70 @@ import { describe, expect, it } from 'vitest';
import { FUNCTION_NAMES_MATH } from '../../function-names';
import { Acosh } from '../index';
import { NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object';
import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object';
import { ArrayValueObject, transformToValue, transformToValueObject } from '../../../../engine/value-object/array-value-object';
import { ErrorType } from '../../../../basics/error-type';
import { ErrorValueObject } from '../../../../engine/value-object/base-value-object';
describe('Test acosh function', () => {
const testFunction = new Acosh(FUNCTION_NAMES_MATH.ACOSH);
describe('Acosh', () => {
it('Value is normal', () => {
it('Value is normal number', () => {
const value = NumberValueObject.create(1);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0);
});
it('Value is string number', () => {
const value = new StringValueObject('1');
it('Value is number valid', () => {
const value = NumberValueObject.create(-2);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.NUM);
});
it('Value is number string', () => {
const value = StringValueObject.create('1.5');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0.9624236501192069);
});
it('Value is normal string', () => {
const value = StringValueObject.create('test');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.VALUE);
});
it('Value is boolean', () => {
const value = BooleanValueObject.create(true);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0);
});
it('Value is blank cell', () => {
const value = NullValueObject.create();
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.NUM);
});
it('Value is error', () => {
const value = ErrorValueObject.create(ErrorType.NAME);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.NAME);
});
it('Value is array', () => {
const valueArray = ArrayValueObject.create({
calculateValueList: transformToValueObject([
[1, ' ', 1.23, true, false],
[0, '100', '2.34', 'test', -3],
[1, ' ', 1.23, true, false, null],
[0, '100', '2.34', 'test', -3, ErrorType.NAME],
]),
rowCount: 2,
columnCount: 5,
columnCount: 6,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
const result = testFunction.calculate(valueArray);
expect(transformToValue(result.getArrayValue())).toStrictEqual([[0, '#VALUE!', 0.6658635291565548, 0, '#NUM!'],
['#NUM!', 5.298292365610484, 1.494153066724473, '#VALUE!', '#NUM!']]);
expect(transformToValue(result.getArrayValue())).toStrictEqual([[0, ErrorType.VALUE, 0.6658635291565548, 0, ErrorType.NUM, ErrorType.NUM], [ErrorType.NUM, 5.298292365610484, 1.494153066724473, ErrorType.VALUE, ErrorType.NUM, ErrorType.NAME]]);
});
});
});
@@ -14,9 +14,7 @@
* limitations under the License.
*/
import { ErrorType } from '../../../basics/error-type';
import type { BaseValueObject } from '../../../engine/value-object/base-value-object';
import { ErrorValueObject } from '../../../engine/value-object/base-value-object';
import { BaseFunction } from '../../base-function';
export class Acosh extends BaseFunction {
@@ -30,7 +28,7 @@ export class Acosh extends BaseFunction {
}
if (variant.isError()) {
return ErrorValueObject.create(ErrorType.VALUE);
return variant;
}
return variant.acosh();
}
@@ -18,41 +18,70 @@ import { describe, expect, it } from 'vitest';
import { FUNCTION_NAMES_MATH } from '../../function-names';
import { Acot } from '../index';
import { NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object';
import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object';
import { ArrayValueObject, transformToValue, transformToValueObject } from '../../../../engine/value-object/array-value-object';
import { ErrorType } from '../../../../basics/error-type';
import { ErrorValueObject } from '../../../../engine/value-object/base-value-object';
describe('Test acot function', () => {
const testFunction = new Acot(FUNCTION_NAMES_MATH.ACOT);
describe('Acot', () => {
it('Value is normal', () => {
it('Value is normal number', () => {
const value = NumberValueObject.create(1);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0.7853981633974483);
});
it('Value is string number', () => {
const value = new StringValueObject('1');
it('Value is number negative', () => {
const value = NumberValueObject.create(-2);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0.7853981633974483);
expect(result.getValue()).toBe(2.677945044588987);
});
it('Value is number string', () => {
const value = StringValueObject.create('1.5');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0.5880026035475675);
});
it('Value is normal string', () => {
const value = StringValueObject.create('test');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.VALUE);
});
it('Value is boolean', () => {
const value = BooleanValueObject.create(false);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(1.5707963267948966);
});
it('Value is blank cell', () => {
const value = NullValueObject.create();
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(1.5707963267948966);
});
it('Value is error', () => {
const value = ErrorValueObject.create(ErrorType.NAME);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.NAME);
});
it('Value is array', () => {
const valueArray = ArrayValueObject.create({
calculateValueList: transformToValueObject([
[1, ' ', 1.23, true, false],
[0, '100', '2.34', 'test', -3],
[1, ' ', 1.23, true, false, null],
[0, '100', '2.34', 'test', -3, ErrorType.NAME],
]),
rowCount: 2,
columnCount: 5,
columnCount: 6,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
const result = testFunction.calculate(valueArray);
expect(transformToValue(result.getArrayValue())).toStrictEqual([[0.7853981633974483, '#VALUE!', 0.682622552417217, 0.7853981633974483, 1.5707963267948966],
[1.5707963267948966, 0.009999666686665238, 0.40385979490737667, '#VALUE!', 2.819842099193151]]);
expect(transformToValue(result.getArrayValue())).toStrictEqual([[0.7853981633974483, ErrorType.VALUE, 0.682622552417217, 0.7853981633974483, 1.5707963267948966, 1.5707963267948966], [1.5707963267948966, 0.009999666686665238, 0.40385979490737667, ErrorType.VALUE, 2.819842099193151, ErrorType.NAME]]);
});
});
});
@@ -0,0 +1,87 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed 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.
*/
import { describe, expect, it } from 'vitest';
import { FUNCTION_NAMES_MATH } from '../../function-names';
import { Asin } from '../index';
import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object';
import { ArrayValueObject, transformToValue, transformToValueObject } from '../../../../engine/value-object/array-value-object';
import { ErrorType } from '../../../../basics/error-type';
import { ErrorValueObject } from '../../../../engine/value-object/base-value-object';
describe('Test asin function', () => {
const testFunction = new Asin(FUNCTION_NAMES_MATH.ASIN);
describe('Asin', () => {
it('Value is normal number', () => {
const value = NumberValueObject.create(1);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(1.5707963267948966);
});
it('Value is number valid', () => {
const value = NumberValueObject.create(-2);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.NUM);
});
it('Value is number string', () => {
const value = StringValueObject.create('0.5');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0.5235987755982989);
});
it('Value is normal string', () => {
const value = StringValueObject.create('test');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.VALUE);
});
it('Value is boolean', () => {
const value = BooleanValueObject.create(false);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0);
});
it('Value is blank cell', () => {
const value = NullValueObject.create();
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0);
});
it('Value is error', () => {
const value = ErrorValueObject.create(ErrorType.NAME);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.NAME);
});
it('Value is array', () => {
const valueArray = ArrayValueObject.create({
calculateValueList: transformToValueObject([
[1, ' ', 1.23, true, false, null],
[0, '100', '2.34', 'test', -3, ErrorType.NAME],
]),
rowCount: 2,
columnCount: 6,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
const result = testFunction.calculate(valueArray);
expect(transformToValue(result.getArrayValue())).toStrictEqual([[1.5707963267948966, '#VALUE!', ErrorType.NUM, 1.5707963267948966, 0, 0], [0, ErrorType.NUM, ErrorType.NUM, '#VALUE!', ErrorType.NUM, '#NAME?']]);
});
});
});
@@ -0,0 +1,36 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed 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.
*/
import type { BaseValueObject } from '../../../engine/value-object/base-value-object';
import { BaseFunction } from '../../base-function';
export class Asin extends BaseFunction {
override minParams = 1;
override maxParams = 1;
override calculate(variant: BaseValueObject) {
if (variant.isString()) {
variant = variant.convertToNumberObjectValue();
}
if (variant.isError()) {
return variant;
}
return variant.asin();
}
}
@@ -0,0 +1,87 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed 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.
*/
import { describe, expect, it } from 'vitest';
import { FUNCTION_NAMES_MATH } from '../../function-names';
import { Asinh } from '../index';
import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object';
import { ArrayValueObject, transformToValue, transformToValueObject } from '../../../../engine/value-object/array-value-object';
import { ErrorType } from '../../../../basics/error-type';
import { ErrorValueObject } from '../../../../engine/value-object/base-value-object';
describe('Test asinh function', () => {
const testFunction = new Asinh(FUNCTION_NAMES_MATH.ASINH);
describe('Asinh', () => {
it('Value is normal number', () => {
const value = NumberValueObject.create(1);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0.881373587019543);
});
it('Value is number negative', () => {
const value = NumberValueObject.create(-2);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(-1.4436354751788103);
});
it('Value is number string', () => {
const value = StringValueObject.create('0.5');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0.48121182505960347);
});
it('Value is normal string', () => {
const value = StringValueObject.create('test');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.VALUE);
});
it('Value is boolean', () => {
const value = BooleanValueObject.create(false);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0);
});
it('Value is blank cell', () => {
const value = NullValueObject.create();
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0);
});
it('Value is error', () => {
const value = ErrorValueObject.create(ErrorType.NAME);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.NAME);
});
it('Value is array', () => {
const valueArray = ArrayValueObject.create({
calculateValueList: transformToValueObject([
[1, ' ', 1.23, true, false, null],
[0, '100', '2.34', 'test', -3, ErrorType.NAME],
]),
rowCount: 2,
columnCount: 6,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
const result = testFunction.calculate(valueArray);
expect(transformToValue(result.getArrayValue())).toStrictEqual([[0.881373587019543, ErrorType.VALUE, 1.0350378961923077, 0.881373587019543, 0, 0], [0, 5.298342365610589, 1.5861119704218916, ErrorType.VALUE, -1.8184464592320668, ErrorType.NAME]]);
});
});
});
@@ -0,0 +1,36 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed 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.
*/
import type { BaseValueObject } from '../../../engine/value-object/base-value-object';
import { BaseFunction } from '../../base-function';
export class Asinh extends BaseFunction {
override minParams = 1;
override maxParams = 1;
override calculate(variant: BaseValueObject) {
if (variant.isString()) {
variant = variant.convertToNumberObjectValue();
}
if (variant.isError()) {
return variant;
}
return variant.asinh();
}
}
@@ -0,0 +1,87 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed 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.
*/
import { describe, expect, it } from 'vitest';
import { FUNCTION_NAMES_MATH } from '../../function-names';
import { Atan } from '../index';
import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object';
import { ArrayValueObject, transformToValue, transformToValueObject } from '../../../../engine/value-object/array-value-object';
import { ErrorType } from '../../../../basics/error-type';
import { ErrorValueObject } from '../../../../engine/value-object/base-value-object';
describe('Test atan function', () => {
const testFunction = new Atan(FUNCTION_NAMES_MATH.ATAN);
describe('Atan', () => {
it('Value is normal number', () => {
const value = NumberValueObject.create(1);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0.7853981633974483);
});
it('Value is number negative', () => {
const value = NumberValueObject.create(-2);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(-1.1071487177940904);
});
it('Value is number string', () => {
const value = StringValueObject.create('0.5');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0.4636476090008061);
});
it('Value is normal string', () => {
const value = StringValueObject.create('test');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.VALUE);
});
it('Value is boolean', () => {
const value = BooleanValueObject.create(false);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0);
});
it('Value is blank cell', () => {
const value = NullValueObject.create();
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0);
});
it('Value is error', () => {
const value = ErrorValueObject.create(ErrorType.NAME);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.NAME);
});
it('Value is array', () => {
const valueArray = ArrayValueObject.create({
calculateValueList: transformToValueObject([
[1, ' ', 1.23, true, false, null],
[0, '100', '2.34', 'test', -3, ErrorType.NAME],
]),
rowCount: 2,
columnCount: 6,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
const result = testFunction.calculate(valueArray);
expect(transformToValue(result.getArrayValue())).toStrictEqual([[0.7853981633974483, ErrorType.VALUE, 0.8881737743776796, 0.7853981633974483, 0, 0], [0, 1.5607966601082315, 1.16693653188752, ErrorType.VALUE, -1.2490457723982544, ErrorType.NAME]]);
});
});
});
@@ -0,0 +1,36 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed 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.
*/
import type { BaseValueObject } from '../../../engine/value-object/base-value-object';
import { BaseFunction } from '../../base-function';
export class Atan extends BaseFunction {
override minParams = 1;
override maxParams = 1;
override calculate(variant: BaseValueObject) {
if (variant.isString()) {
variant = variant.convertToNumberObjectValue();
}
if (variant.isError()) {
return variant;
}
return variant.atan();
}
}
@@ -0,0 +1,136 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed 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.
*/
import { describe, expect, it } from 'vitest';
import { FUNCTION_NAMES_MATH } from '../../function-names';
import { Atan2 } from '../index';
import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object';
import { ArrayValueObject, transformToValue, transformToValueObject } from '../../../../engine/value-object/array-value-object';
import { ErrorType } from '../../../../basics/error-type';
import { ErrorValueObject } from '../../../../engine/value-object/base-value-object';
describe('Test atan2 function', () => {
const testFunction = new Atan2(FUNCTION_NAMES_MATH.ATAN2);
describe('Atan2', () => {
it('Value is normal number', () => {
const xNum = NumberValueObject.create(1);
const yNum = NumberValueObject.create(2);
const result = testFunction.calculate(xNum, yNum);
expect(result.getValue()).toBe(1.1071487177940904);
});
it('Value is normal number 2', () => {
const xNum = NumberValueObject.create(8);
const yNum = NumberValueObject.create(9);
const result = testFunction.calculate(xNum, yNum);
expect(result.getValue()).toBe(0.844153986113171);
});
it('Value is number valid', () => {
const xNum = NumberValueObject.create(-2);
const yNum = NumberValueObject.create(-3);
const result = testFunction.calculate(xNum, yNum);
expect(result.getValue()).toBe(-2.1587989303424644);
});
it('Value is number string', () => {
const xNum = StringValueObject.create('0.5');
const yNum = NumberValueObject.create(2);
const result = testFunction.calculate(xNum, yNum);
expect(result.getValue()).toBe(1.3258176636680326);
});
it('Value is normal string', () => {
const xNum = StringValueObject.create('test');
const yNum = NumberValueObject.create(1);
const result = testFunction.calculate(xNum, yNum);
expect(result.getValue()).toBe(ErrorType.VALUE);
});
it('Value is boolean', () => {
const xNum = BooleanValueObject.create(false);
const yNum = NumberValueObject.create(1);
const result = testFunction.calculate(xNum, yNum);
expect(result.getValue()).toBe(1.5707963267948966);
});
it('Value is blank cell', () => {
const xNum = NullValueObject.create();
const yNum = NumberValueObject.create(1);
const result = testFunction.calculate(xNum, yNum);
expect(result.getValue()).toBe(1.5707963267948966);
});
it('Value is error', () => {
const xNum = ErrorValueObject.create(ErrorType.NAME);
const yNum = NumberValueObject.create(1);
const result = testFunction.calculate(xNum, yNum);
expect(result.getValue()).toBe(ErrorType.NAME);
});
it('Result is error', () => {
const xNum = NumberValueObject.create(0);
const yNum = NumberValueObject.create(0);
const result = testFunction.calculate(xNum, yNum);
expect(result.getValue()).toBe(ErrorType.DIV_BY_ZERO);
});
it('Value is array and number', () => {
const xNum = ArrayValueObject.create({
calculateValueList: transformToValueObject([
[1, ' ', 1.23, true, false, null],
[0, '100', '2.34', 'test', -3, ErrorType.NAME],
]),
rowCount: 2,
columnCount: 6,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
const yNum = NumberValueObject.create(1);
const result = testFunction.calculate(xNum, yNum);
expect(transformToValue(result.getArrayValue())).toStrictEqual([[0.7853981633974483, ErrorType.VALUE, 0.682622552417217, 0.7853981633974483, 1.5707963267948966, 1.5707963267948966], [1.5707963267948966, 0.009999666686665238, 0.40385979490737667, ErrorType.VALUE, 2.819842099193151, ErrorType.NAME]]);
});
it('Value is array and array', () => {
const xNum = ArrayValueObject.create({
calculateValueList: transformToValueObject([
[1],
[2],
[3],
]),
rowCount: 3,
columnCount: 1,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
const yNum = ArrayValueObject.create({
calculateValueList: transformToValueObject([
[1, ' ', 1.23, true, false, null],
[0, '100', '2.34', 'test', -3, ErrorType.NAME],
]),
rowCount: 2,
columnCount: 6,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
const result = testFunction.calculate(xNum, yNum);
expect(transformToValue(result.getArrayValue())).toStrictEqual([[0.7853981633974483, ErrorType.VALUE, 0.8881737743776796, 0.7853981633974483, 0, 0], [0, 1.550798992821746, 0.8635794970038352, ErrorType.VALUE, -0.982793723247329, ErrorType.NAME], [ErrorType.NA, ErrorType.NA, ErrorType.NA, ErrorType.NA, ErrorType.NA, ErrorType.NA]]);
});
});
});
@@ -0,0 +1,45 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed 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.
*/
import type { BaseValueObject } from '../../../engine/value-object/base-value-object';
import { BaseFunction } from '../../base-function';
export class Atan2 extends BaseFunction {
override minParams = 2;
override maxParams = 2;
override calculate(xNum: BaseValueObject, yNum: BaseValueObject) {
if (xNum.isString()) {
xNum = xNum.convertToNumberObjectValue();
}
if (xNum.isError()) {
return xNum;
}
if (yNum.isString()) {
yNum = yNum.convertToNumberObjectValue();
}
if (yNum.isError()) {
return yNum;
}
// Note that the order of parameters is different from JavaScript
return yNum.atan2(xNum);
}
}
@@ -0,0 +1,87 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed 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.
*/
import { describe, expect, it } from 'vitest';
import { FUNCTION_NAMES_MATH } from '../../function-names';
import { Atanh } from '../index';
import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object';
import { ArrayValueObject, transformToValue, transformToValueObject } from '../../../../engine/value-object/array-value-object';
import { ErrorType } from '../../../../basics/error-type';
import { ErrorValueObject } from '../../../../engine/value-object/base-value-object';
describe('Test atanh function', () => {
const testFunction = new Atanh(FUNCTION_NAMES_MATH.ATANH);
describe('Atanh', () => {
it('Value is normal number', () => {
const value = NumberValueObject.create(0.1);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0.10033534773107558);
});
it('Value is number negative', () => {
const value = NumberValueObject.create(-2);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.NUM);
});
it('Value is number string', () => {
const value = StringValueObject.create('0.5');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0.5493061443340548);
});
it('Value is normal string', () => {
const value = StringValueObject.create('test');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.VALUE);
});
it('Value is boolean', () => {
const value = BooleanValueObject.create(false);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0);
});
it('Value is blank cell', () => {
const value = NullValueObject.create();
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0);
});
it('Value is error', () => {
const value = ErrorValueObject.create(ErrorType.NAME);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.NAME);
});
it('Value is array', () => {
const valueArray = ArrayValueObject.create({
calculateValueList: transformToValueObject([
[1, ' ', 1.23, true, false, null],
[0, '100', '2.34', 'test', -3, ErrorType.NAME],
]),
rowCount: 2,
columnCount: 6,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
const result = testFunction.calculate(valueArray);
expect(transformToValue(result.getArrayValue())).toStrictEqual([[ErrorType.NUM, ErrorType.VALUE, ErrorType.NUM, ErrorType.NUM, 0, 0], [0, ErrorType.NUM, ErrorType.NUM, ErrorType.VALUE, ErrorType.NUM, ErrorType.NAME]]);
});
});
});
@@ -0,0 +1,36 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed 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.
*/
import type { BaseValueObject } from '../../../engine/value-object/base-value-object';
import { BaseFunction } from '../../base-function';
export class Atanh extends BaseFunction {
override minParams = 1;
override maxParams = 1;
override calculate(variant: BaseValueObject) {
if (variant.isString()) {
variant = variant.convertToNumberObjectValue();
}
if (variant.isError()) {
return variant;
}
return variant.atanh();
}
}
@@ -0,0 +1,87 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed 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.
*/
import { describe, expect, it } from 'vitest';
import { FUNCTION_NAMES_MATH } from '../../function-names';
import { Cos } from '../index';
import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object';
import { ArrayValueObject, transformToValue, transformToValueObject } from '../../../../engine/value-object/array-value-object';
import { ErrorType } from '../../../../basics/error-type';
import { ErrorValueObject } from '../../../../engine/value-object/base-value-object';
describe('Test cos function', () => {
const testFunction = new Cos(FUNCTION_NAMES_MATH.COS);
describe('Cos', () => {
it('Value is normal number', () => {
const value = NumberValueObject.create(1);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0.5403023058681398);
});
it('Value is number negative', () => {
const value = NumberValueObject.create(-2);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(-0.4161468365471424);
});
it('Value is number string', () => {
const value = StringValueObject.create('0.5');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0.8775825618903728);
});
it('Value is normal string', () => {
const value = StringValueObject.create('test');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.VALUE);
});
it('Value is boolean', () => {
const value = BooleanValueObject.create(false);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(1);
});
it('Value is blank cell', () => {
const value = NullValueObject.create();
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(1);
});
it('Value is error', () => {
const value = ErrorValueObject.create(ErrorType.NAME);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.NAME);
});
it('Value is array', () => {
const valueArray = ArrayValueObject.create({
calculateValueList: transformToValueObject([
[1, ' ', 1.23, true, false, null],
[0, '100', '2.34', 'test', -3, ErrorType.NAME],
]),
rowCount: 2,
columnCount: 6,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
const result = testFunction.calculate(valueArray);
expect(transformToValue(result.getArrayValue())).toStrictEqual([[0.5403023058681398, ErrorType.VALUE, 0.3342377271245026, 0.5403023058681398, 1, 1], [1, 0.8623188722876839, -0.695563326462902, ErrorType.VALUE, -0.9899924966004454, ErrorType.NAME]]);
});
});
});
@@ -0,0 +1,36 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed 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.
*/
import type { BaseValueObject } from '../../../engine/value-object/base-value-object';
import { BaseFunction } from '../../base-function';
export class Cos extends BaseFunction {
override minParams = 1;
override maxParams = 1;
override calculate(variant: BaseValueObject) {
if (variant.isString()) {
variant = variant.convertToNumberObjectValue();
}
if (variant.isError()) {
return variant;
}
return variant.cos();
}
}
@@ -18,6 +18,12 @@ import { Abs } from './abs';
import { Acos } from './acos';
import { Acosh } from './acosh';
import { Acot } from './acot';
import { Asin } from './asin';
import { Asinh } from './asinh';
import { Atan } from './atan';
import { Atan2 } from './atan2';
import { Atanh } from './atanh';
import { Cos } from './cos';
import { FUNCTION_NAMES_MATH } from './function-names';
import { Mod } from './mod';
import { Power } from './power';
@@ -26,17 +32,27 @@ import { Subtotal } from './subtotal';
import { Sum } from './sum';
import { Sumif } from './sumif';
import { Sumifs } from './sumifs';
import { Tan } from './tan';
import { Tanh } from './tanh';
export const functionMath = [
[Abs, FUNCTION_NAMES_MATH.ABS],
[Acos, FUNCTION_NAMES_MATH.ACOS],
[Acosh, FUNCTION_NAMES_MATH.ACOSH],
[Acot, FUNCTION_NAMES_MATH.ACOT],
[Asin, FUNCTION_NAMES_MATH.ASIN],
[Asinh, FUNCTION_NAMES_MATH.ASINH],
[Atan, FUNCTION_NAMES_MATH.ATAN],
[Atan2, FUNCTION_NAMES_MATH.ATAN2],
[Atanh, FUNCTION_NAMES_MATH.ATANH],
[Cos, FUNCTION_NAMES_MATH.COS],
[Mod, FUNCTION_NAMES_MATH.MOD],
[Power, FUNCTION_NAMES_MATH.POWER],
[Product, FUNCTION_NAMES_MATH.PRODUCT],
[Subtotal, FUNCTION_NAMES_MATH.SUBTOTAL],
[Sum, FUNCTION_NAMES_MATH.SUM],
[Sumif, FUNCTION_NAMES_MATH.SUMIF],
[Sumifs, FUNCTION_NAMES_MATH.SUMIFS],
[Power, FUNCTION_NAMES_MATH.POWER],
[Product, FUNCTION_NAMES_MATH.PRODUCT],
[Tan, FUNCTION_NAMES_MATH.TAN],
[Tanh, FUNCTION_NAMES_MATH.TANH],
];
@@ -0,0 +1,87 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed 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.
*/
import { describe, expect, it } from 'vitest';
import { FUNCTION_NAMES_MATH } from '../../function-names';
import { Tan } from '../index';
import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object';
import { ArrayValueObject, transformToValue, transformToValueObject } from '../../../../engine/value-object/array-value-object';
import { ErrorType } from '../../../../basics/error-type';
import { ErrorValueObject } from '../../../../engine/value-object/base-value-object';
describe('Test tan function', () => {
const testFunction = new Tan(FUNCTION_NAMES_MATH.TAN);
describe('Tan', () => {
it('Value is normal number', () => {
const value = NumberValueObject.create(1);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(1.5574077246549023);
});
it('Value is number negative', () => {
const value = NumberValueObject.create(-2);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(2.185039863261519);
});
it('Value is number string', () => {
const value = StringValueObject.create('0.5');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0.5463024898437905);
});
it('Value is normal string', () => {
const value = StringValueObject.create('test');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.VALUE);
});
it('Value is boolean', () => {
const value = BooleanValueObject.create(false);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0);
});
it('Value is blank cell', () => {
const value = NullValueObject.create();
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0);
});
it('Value is error', () => {
const value = ErrorValueObject.create(ErrorType.NAME);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.NAME);
});
it('Value is array', () => {
const valueArray = ArrayValueObject.create({
calculateValueList: transformToValueObject([
[1, ' ', 1.23, true, false, null],
[0, '100', '2.34', 'test', -3, ErrorType.NAME],
]),
rowCount: 2,
columnCount: 6,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
const result = testFunction.calculate(valueArray);
expect(transformToValue(result.getArrayValue())).toStrictEqual([[1.5574077246549023, ErrorType.VALUE, 2.819815734268152, 1.5574077246549023, 0, 0], [0, -0.5872139151569291, -1.032925063376592, ErrorType.VALUE, 0.1425465430742778, ErrorType.NAME]]);
});
});
});
@@ -0,0 +1,36 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed 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.
*/
import type { BaseValueObject } from '../../../engine/value-object/base-value-object';
import { BaseFunction } from '../../base-function';
export class Tan extends BaseFunction {
override minParams = 1;
override maxParams = 1;
override calculate(variant: BaseValueObject) {
if (variant.isString()) {
variant = variant.convertToNumberObjectValue();
}
if (variant.isError()) {
return variant;
}
return variant.tan();
}
}
@@ -0,0 +1,87 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed 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.
*/
import { describe, expect, it } from 'vitest';
import { FUNCTION_NAMES_MATH } from '../../function-names';
import { Tanh } from '../index';
import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object';
import { ArrayValueObject, transformToValue, transformToValueObject } from '../../../../engine/value-object/array-value-object';
import { ErrorType } from '../../../../basics/error-type';
import { ErrorValueObject } from '../../../../engine/value-object/base-value-object';
describe('Test tanh function', () => {
const testFunction = new Tanh(FUNCTION_NAMES_MATH.TANH);
describe('Tanh', () => {
it('Value is normal number', () => {
const value = NumberValueObject.create(1);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0.7615941559557649);
});
it('Value is number negative', () => {
const value = NumberValueObject.create(-2);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(-0.9640275800758169);
});
it('Value is number string', () => {
const value = StringValueObject.create('0.5');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0.46211715726000974);
});
it('Value is normal string', () => {
const value = StringValueObject.create('test');
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.VALUE);
});
it('Value is boolean', () => {
const value = BooleanValueObject.create(false);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0);
});
it('Value is blank cell', () => {
const value = NullValueObject.create();
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(0);
});
it('Value is error', () => {
const value = ErrorValueObject.create(ErrorType.NAME);
const result = testFunction.calculate(value);
expect(result.getValue()).toBe(ErrorType.NAME);
});
it('Value is array', () => {
const valueArray = ArrayValueObject.create({
calculateValueList: transformToValueObject([
[1, ' ', 1.23, true, false, null],
[0, '100', '2.34', 'test', -3, ErrorType.NAME],
]),
rowCount: 2,
columnCount: 6,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
const result = testFunction.calculate(valueArray);
expect(transformToValue(result.getArrayValue())).toStrictEqual([[0.7615941559557649, ErrorType.VALUE, 0.8425793256589296, 0.7615941559557649, 0, 0], [0, 1, 0.9816125892654238, ErrorType.VALUE, -0.9950547536867305, ErrorType.NAME]]);
});
});
});
@@ -0,0 +1,36 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed 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.
*/
import type { BaseValueObject } from '../../../engine/value-object/base-value-object';
import { BaseFunction } from '../../base-function';
export class Tanh extends BaseFunction {
override minParams = 1;
override maxParams = 1;
override calculate(variant: BaseValueObject) {
if (variant.isString()) {
variant = variant.convertToNumberObjectValue();
}
if (variant.isError()) {
return variant;
}
return variant.tanh();
}
}
@@ -17,7 +17,7 @@
import { LifecycleStages, OnLifecycle, RxDisposable } from '@univerjs/core';
import { Inject } from '@wendellhu/redi';
import { INTERCEPTOR_POINT, SheetInterceptorService } from '@univerjs/sheets';
import { extractFormulaError, extractFormulaNumber } from './utils/utils';
import { extractFormulaError } from './utils/utils';
const FORMULA_ERROR_MARK = {
tl: {
@@ -41,15 +41,6 @@ export class FormulaRenderManagerController extends RxDisposable {
INTERCEPTOR_POINT.CELL_CONTENT,
{
handler: (cell, pos, next) => {
// Dealing with precision issues
const formulaNumber = extractFormulaNumber(cell);
if (formulaNumber !== null) {
return next({
...cell,
v: formulaNumber,
});
}
const errorType = extractFormulaError(cell);
if (!errorType) {
return next(cell);
@@ -113,7 +113,7 @@ export default {
},
},
ASIN: {
description: 'Returns the arcsine of a number',
description: 'Returns the arcsine of a number.',
abstract: 'Returns the arcsine of a number',
links: [
{
@@ -122,12 +122,11 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
number: { name: 'number', detail: 'The sine of the angle you want and must be from -1 to 1.' },
},
},
ASINH: {
description: 'Returns the inverse hyperbolic sine of a number',
description: 'Returns the inverse hyperbolic sine of a number.',
abstract: 'Returns the inverse hyperbolic sine of a number',
links: [
{
@@ -136,12 +135,11 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
number: { name: 'number', detail: 'Any real number.' },
},
},
ATAN: {
description: 'Returns the arctangent of a number',
description: 'Returns the arctangent of a number.',
abstract: 'Returns the arctangent of a number',
links: [
{
@@ -150,12 +148,11 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
number: { name: 'number', detail: 'The tangent of the angle you want.' },
},
},
ATAN2: {
description: 'Returns the arctangent from x- and y-coordinates',
description: 'Returns the arctangent from x- and y-coordinates.',
abstract: 'Returns the arctangent from x- and y-coordinates',
links: [
{
@@ -164,12 +161,12 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
xNum: { name: 'x_num', detail: 'The x-coordinate of the point.' },
yNum: { name: 'y_num', detail: 'The y-coordinate of the point.' },
},
},
ATANH: {
description: 'Returns the inverse hyperbolic tangent of a number',
description: 'Returns the inverse hyperbolic tangent of a number.',
abstract: 'Returns the inverse hyperbolic tangent of a number',
links: [
{
@@ -178,8 +175,7 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
number: { name: 'number', detail: 'Any real number between 1 and -1.' },
},
},
BASE: {
@@ -267,7 +263,7 @@ export default {
},
},
COS: {
description: 'Returns the cosine of a number',
description: 'Returns the cosine of a number.',
abstract: 'Returns the cosine of a number',
links: [
{
@@ -276,8 +272,7 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
number: { name: 'number', detail: 'The angle in radians for which you want the cosine.' },
},
},
COSH: {
@@ -1141,7 +1136,7 @@ export default {
},
},
TAN: {
description: 'Returns the tangent of a number',
description: 'Returns the tangent of a number.',
abstract: 'Returns the tangent of a number',
links: [
{
@@ -1150,12 +1145,11 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
number: { name: 'number', detail: 'The angle in radians for which you want the tangent.' },
},
},
TANH: {
description: 'Returns the hyperbolic tangent of a number',
description: 'Returns the hyperbolic tangent of a number.',
abstract: 'Returns the hyperbolic tangent of a number',
links: [
{
@@ -1164,8 +1158,7 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
number: { name: 'number', detail: 'Any real number.' },
},
},
TRUNC: {
@@ -114,7 +114,7 @@ export default {
},
ASIN: {
description: '数値のアークサインを返します。',
abstract: '数値のアークサインを返します。',
abstract: '数値のアークサインを返します',
links: [
{
title: '指導',
@@ -122,13 +122,12 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
number: { name: '数値', detail: '求める角度のサインの値を -1 ~ 1 の範囲で指定します。' },
},
},
ASINH: {
description: '数値の双曲線逆正弦 (ハイパーボリック サインの逆関数) を返します。',
abstract: '数値の双曲線逆正弦 (ハイパーボリック サインの逆関数) を返します。',
abstract: '数値の双曲線逆正弦 (ハイパーボリック サインの逆関数) を返します',
links: [
{
title: '指導',
@@ -136,13 +135,12 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
number: { name: '数値', detail: '実数を指定します。' },
},
},
ATAN: {
description: '数値のアークタンジェントを返します。',
abstract: '数値のアークタンジェントを返します。',
abstract: '数値のアークタンジェントを返します',
links: [
{
title: '指導',
@@ -150,13 +148,12 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
number: { name: '数値', detail: '求める角度のタンジェントの値を指定します。' },
},
},
ATAN2: {
description: '指定された x-y 座標のアークタンジェントを返します。',
abstract: '指定された x-y 座標のアークタンジェントを返します。',
abstract: '指定された x-y 座標のアークタンジェントを返します',
links: [
{
title: '指導',
@@ -164,13 +161,13 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
xNum: { name: 'x 座標', detail: '点の x 座標を指定します。' },
yNum: { name: 'y 座標', detail: '点の y 座標を指定します。' },
},
},
ATANH: {
description: '数値の双曲線逆正接 (ハイパーボリック タンジェントの逆関数) を返します。',
abstract: '数値の双曲線逆正接 (ハイパーボリック タンジェントの逆関数) を返します。',
abstract: '数値の双曲線逆正接 (ハイパーボリック タンジェントの逆関数) を返します',
links: [
{
title: '指導',
@@ -178,8 +175,7 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
number: { name: '数値', detail: '-1 より大きく 1 より小さい実数を指定します。' },
},
},
BASE: {
@@ -268,7 +264,7 @@ export default {
},
COS: {
description: '指定された角度のコサインを返します。',
abstract: '指定された角度のコサインを返します。',
abstract: '指定された角度のコサインを返します',
links: [
{
title: '指導',
@@ -276,8 +272,7 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
number: { name: '数値', detail: 'コサインを求める角度をラジアンで指定します。' },
},
},
COSH: {
@@ -1133,7 +1128,7 @@ export default {
},
TAN: {
description: '指定された角度のタンジェントを返します。',
abstract: '指定された角度のタンジェントを返します。',
abstract: '指定された角度のタンジェントを返します',
links: [
{
title: '指導',
@@ -1141,13 +1136,12 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
number: { name: '数値', detail: 'タンジェントを求める角度をラジアンを単位として指定します。' },
},
},
TANH: {
description: '数値の双曲線正接 (ハイパーボリック タンジェント) を返します。',
abstract: '数値の双曲線正接 (ハイパーボリック タンジェント) を返します。',
abstract: '数値の双曲線正接 (ハイパーボリック タンジェント) を返します',
links: [
{
title: '指導',
@@ -1155,8 +1149,7 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
number: { name: '数値', detail: '実数を指定します。' },
},
},
TRUNC: {
@@ -112,7 +112,7 @@ export default {
},
},
ASIN: {
description: '返回数字的反正弦值',
description: '返回数字的反正弦值。',
abstract: '返回数字的反正弦值',
links: [
{
@@ -121,12 +121,11 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
number: { name: '数值', detail: ' 所求角度的正弦值,必须介于 -1 到 1 之间。' },
},
},
ASINH: {
description: '返回数字的反双曲正弦值',
description: '返回数字的反双曲正弦值。',
abstract: '返回数字的反双曲正弦值',
links: [
{
@@ -135,12 +134,11 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
number: { name: '数值', detail: '任意实数。' },
},
},
ATAN: {
description: '返回数字的反正切值',
description: '返回数字的反正切值。',
abstract: '返回数字的反正切值',
links: [
{
@@ -149,12 +147,11 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
number: { name: '数值', detail: '所求角度的正切值。' },
},
},
ATAN2: {
description: '返回 X 和 Y 坐标的反正切值',
description: '返回 X 和 Y 坐标的反正切值。',
abstract: '返回 X 和 Y 坐标的反正切值',
links: [
{
@@ -163,12 +160,12 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
xNum: { name: 'x 坐标', detail: '点的 x 坐标。' },
yNum: { name: 'y 坐标', detail: '点的 y 坐标。' },
},
},
ATANH: {
description: '返回数字的反双曲正切值',
description: '返回数字的反双曲正切值。',
abstract: '返回数字的反双曲正切值',
links: [
{
@@ -177,8 +174,7 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
number: { name: '数值', detail: '-1 到 1 之间的任意实数。' },
},
},
BASE: {
@@ -266,7 +262,7 @@ export default {
},
},
COS: {
description: '返回数字的余弦值',
description: '返回数字的余弦值。',
abstract: '返回数字的余弦值',
links: [
{
@@ -275,8 +271,7 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
number: { name: '数值', detail: '想要求余弦的角度,以弧度表示。' },
},
},
COSH: {
@@ -1142,7 +1137,7 @@ export default {
},
},
TAN: {
description: '返回数字的正切值',
description: '返回数字的正切值。',
abstract: '返回数字的正切值',
links: [
{
@@ -1151,12 +1146,11 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
number: { name: '数值', detail: '要求正切的角度,以弧度表示。' },
},
},
TANH: {
description: '返回数字的双曲正切值',
description: '返回数字的双曲正切值。',
abstract: '返回数字的双曲正切值',
links: [
{
@@ -1165,8 +1159,7 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
number: { name: '数值', detail: '任意实数。' },
},
},
TRUNC: {
@@ -151,16 +151,9 @@ export const FUNCTION_LIST_MATH: IFunctionInfo[] = [
abstract: 'formula.functionList.ASIN.abstract',
functionParameter: [
{
name: 'formula.functionList.ASIN.functionParameter.number1.name',
detail: 'formula.functionList.ASIN.functionParameter.number1.detail',
example: 'A1:A20',
require: 1,
repeat: 0,
},
{
name: 'formula.functionList.ASIN.functionParameter.number2.name',
detail: 'formula.functionList.ASIN.functionParameter.number2.detail',
example: 'A1:A20',
name: 'formula.functionList.ASIN.functionParameter.number.name',
detail: 'formula.functionList.ASIN.functionParameter.number.detail',
example: '0',
require: 1,
repeat: 0,
},
@@ -173,16 +166,9 @@ export const FUNCTION_LIST_MATH: IFunctionInfo[] = [
abstract: 'formula.functionList.ASINH.abstract',
functionParameter: [
{
name: 'formula.functionList.ASINH.functionParameter.number1.name',
detail: 'formula.functionList.ASINH.functionParameter.number1.detail',
example: 'A1:A20',
require: 1,
repeat: 0,
},
{
name: 'formula.functionList.ASINH.functionParameter.number2.name',
detail: 'formula.functionList.ASINH.functionParameter.number2.detail',
example: 'A1:A20',
name: 'formula.functionList.ASINH.functionParameter.number.name',
detail: 'formula.functionList.ASINH.functionParameter.number.detail',
example: '10',
require: 1,
repeat: 0,
},
@@ -195,16 +181,9 @@ export const FUNCTION_LIST_MATH: IFunctionInfo[] = [
abstract: 'formula.functionList.ATAN.abstract',
functionParameter: [
{
name: 'formula.functionList.ATAN.functionParameter.number1.name',
detail: 'formula.functionList.ATAN.functionParameter.number1.detail',
example: 'A1:A20',
require: 1,
repeat: 0,
},
{
name: 'formula.functionList.ATAN.functionParameter.number2.name',
detail: 'formula.functionList.ATAN.functionParameter.number2.detail',
example: 'A1:A20',
name: 'formula.functionList.ATAN.functionParameter.number.name',
detail: 'formula.functionList.ATAN.functionParameter.number.detail',
example: '1',
require: 1,
repeat: 0,
},
@@ -217,16 +196,16 @@ export const FUNCTION_LIST_MATH: IFunctionInfo[] = [
abstract: 'formula.functionList.ATAN2.abstract',
functionParameter: [
{
name: 'formula.functionList.ATAN2.functionParameter.number1.name',
detail: 'formula.functionList.ATAN2.functionParameter.number1.detail',
example: 'A1:A20',
name: 'formula.functionList.ATAN2.functionParameter.xNum.name',
detail: 'formula.functionList.ATAN2.functionParameter.xNum.detail',
example: '4',
require: 1,
repeat: 0,
},
{
name: 'formula.functionList.ATAN2.functionParameter.number2.name',
detail: 'formula.functionList.ATAN2.functionParameter.number2.detail',
example: 'A1:A20',
name: 'formula.functionList.ATAN2.functionParameter.yNum.name',
detail: 'formula.functionList.ATAN2.functionParameter.yNum.detail',
example: '3',
require: 1,
repeat: 0,
},
@@ -239,16 +218,9 @@ export const FUNCTION_LIST_MATH: IFunctionInfo[] = [
abstract: 'formula.functionList.ATANH.abstract',
functionParameter: [
{
name: 'formula.functionList.ATANH.functionParameter.number1.name',
detail: 'formula.functionList.ATANH.functionParameter.number1.detail',
example: 'A1:A20',
require: 1,
repeat: 0,
},
{
name: 'formula.functionList.ATANH.functionParameter.number2.name',
detail: 'formula.functionList.ATANH.functionParameter.number2.detail',
example: 'A1:A20',
name: 'formula.functionList.ATANH.functionParameter.number.name',
detail: 'formula.functionList.ATANH.functionParameter.number.detail',
example: '0.1',
require: 1,
repeat: 0,
},
@@ -393,16 +365,9 @@ export const FUNCTION_LIST_MATH: IFunctionInfo[] = [
abstract: 'formula.functionList.COS.abstract',
functionParameter: [
{
name: 'formula.functionList.COS.functionParameter.number1.name',
detail: 'formula.functionList.COS.functionParameter.number1.detail',
example: 'A1:A20',
require: 1,
repeat: 0,
},
{
name: 'formula.functionList.COS.functionParameter.number2.name',
detail: 'formula.functionList.COS.functionParameter.number2.detail',
example: 'A1:A20',
name: 'formula.functionList.COS.functionParameter.number.name',
detail: 'formula.functionList.COS.functionParameter.number.detail',
example: '1',
require: 1,
repeat: 0,
},
@@ -1772,16 +1737,9 @@ export const FUNCTION_LIST_MATH: IFunctionInfo[] = [
abstract: 'formula.functionList.TAN.abstract',
functionParameter: [
{
name: 'formula.functionList.TAN.functionParameter.number1.name',
detail: 'formula.functionList.TAN.functionParameter.number1.detail',
example: 'A1:A20',
require: 1,
repeat: 0,
},
{
name: 'formula.functionList.TAN.functionParameter.number2.name',
detail: 'formula.functionList.TAN.functionParameter.number2.detail',
example: 'A1:A20',
name: 'formula.functionList.TAN.functionParameter.number.name',
detail: 'formula.functionList.TAN.functionParameter.number.detail',
example: '1',
require: 1,
repeat: 0,
},
@@ -1794,16 +1752,9 @@ export const FUNCTION_LIST_MATH: IFunctionInfo[] = [
abstract: 'formula.functionList.TANH.abstract',
functionParameter: [
{
name: 'formula.functionList.TANH.functionParameter.number1.name',
detail: 'formula.functionList.TANH.functionParameter.number1.detail',
example: 'A1:A20',
require: 1,
repeat: 0,
},
{
name: 'formula.functionList.TANH.functionParameter.number2.name',
detail: 'formula.functionList.TANH.functionParameter.number2.detail',
example: 'A1:A20',
name: 'formula.functionList.TANH.functionParameter.number.name',
detail: 'formula.functionList.TANH.functionParameter.number.detail',
example: '1',
require: 1,
repeat: 0,
},
@@ -0,0 +1,60 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed 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.
*/
import { CellValueType, Disposable, LifecycleStages, OnLifecycle } from '@univerjs/core';
import { stripErrorMargin } from '@univerjs/engine-formula';
import { Inject } from '@wendellhu/redi';
import { SheetInterceptorService } from '../services/sheet-interceptor/sheet-interceptor.service';
import { INTERCEPTOR_POINT } from '../services/sheet-interceptor/interceptor-const';
@OnLifecycle(LifecycleStages.Ready, NumberCellDisplayController)
export class NumberCellDisplayController extends Disposable {
constructor(
@Inject(SheetInterceptorService) private _sheetInterceptorService: SheetInterceptorService) {
super();
this._initialize();
}
private _initialize(): void {
this._initInterceptorCellContent();
}
private _initInterceptorCellContent() {
this.disposeWithMe(
this._sheetInterceptorService.intercept(INTERCEPTOR_POINT.CELL_CONTENT, {
priority: 11,
handler: (cell, location, next) => {
// Skip if the cell contains a numfmt pattern
const style = location.workbook.getStyles().getStyleByCell(cell);
if (style?.n?.pattern) {
return next({ ...cell });
}
// Dealing with precision issues
if (cell?.t === CellValueType.NUMBER && typeof cell?.v === 'number') {
return next({
...cell,
v: stripErrorMargin(cell.v),
});
}
return next({ ...cell });
},
})
);
}
}
+2
View File
@@ -39,6 +39,7 @@ import { RangeProtectionRuleModel } from './model/range-protection-rule.model';
import { RangeProtectionRefRangeService } from './services/permission/range-permission/range-protection.ref-range';
import { RangeProtectionService } from './services/permission/range-permission/range-protection.service';
import { ONLY_REGISTER_FORMULA_RELATED_MUTATIONS_KEY } from './controllers/config';
import { NumberCellDisplayController } from './controllers/number-cell.controller';
const PLUGIN_NAME = 'SHEET_PLUGIN';
@@ -96,6 +97,7 @@ export class UniverSheetsPlugin extends Plugin {
// controllers
[BasicWorksheetController],
[MergeCellController],
[NumberCellDisplayController],
[DefinedNameDataController],
// permission