Skip to content

Commit

Permalink
feat: adds ceil, floor, and round functions
Browse files Browse the repository at this point in the history
Includes tests, version bump, and update entry
  • Loading branch information
mgreminger committed Nov 1, 2023
1 parent 5cd0e04 commit dad7e9c
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 10 deletions.
18 changes: 16 additions & 2 deletions public/dimensional_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@
Subs,
Pow,
MatMul,
Eq
Eq,
floor,
ceiling
)

class ExprWithAssumptions(Expr):
Expand Down Expand Up @@ -681,6 +683,12 @@ def ensure_unitless_in_angle_out(arg):
else:
raise TypeError('Unitless input argument required for function')

def ensure_unitless_in(arg):
if dimsys_SI.get_dimensional_dependencies(arg) == {}:
return arg
else:
raise TypeError('Unitless input argument required for function')

def ensure_any_unit_in_angle_out(arg):
# ensure input arg units make sense (will raise if inconsistent)
dimsys_SI.get_dimensional_dependencies(arg)
Expand Down Expand Up @@ -733,6 +741,9 @@ def custom_matmul(exp1: Expr, exp2: Expr):
else:
return MatMul(exp1, exp2)

def custom_round(expression: Expr):
return expression.round()

class PlaceholderFunction(TypedDict):
dim_func: Callable
sympy_func: object
Expand Down Expand Up @@ -780,7 +791,10 @@ def custom_dot(exp1: Matrix, exp2: Matrix):
Function('_IndexMatrix') : {"dim_func": IndexMatrix, "sympy_func": IndexMatrix},
Function('_Eq') : {"dim_func": Eq, "sympy_func": Eq},
Function('_norm') : {"dim_func": custom_norm, "sympy_func": custom_norm},
Function('_dot') : {"dim_func": custom_dot, "sympy_func": custom_dot}
Function('_dot') : {"dim_func": custom_dot, "sympy_func": custom_dot},
Function('_ceil') : {"dim_func": ensure_unitless_in, "sympy_func": ceiling},
Function('_floor') : {"dim_func": ensure_unitless_in, "sympy_func": floor},
Function('_round') : {"dim_func": ensure_unitless_in, "sympy_func": custom_round},
}

placeholder_set = set(placeholder_map.keys())
Expand Down
2 changes: 1 addition & 1 deletion src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
const apiUrl = window.location.origin;
const currentVersion = 20230930;
const currentVersion = 20231031;
const tutorialHash = "fFjTsnFoSQMLwcvteVoNtL";
const termsVersion = 20230608;
Expand Down
15 changes: 15 additions & 0 deletions src/Updates.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,21 @@ import { modifierKey } from "./stores";
}
</style>

<em>October 31, 2023</em>
<h4>New <em>ceil</em>, <em>floor</em>, and <em>round</em> Functions Have Been Added</h4>
<p>
Three new rounding functions have been added. <em>ceil</em> to round up,
<em>floor</em> to round down, and <em>round</em> to round to the nearest integer.
These new functions can be found on the f(x) tab of the virtual keyboard.
Note that these functions require that the input parameter is unitless. An input
with units would lead to unexpected results since all calculation are performed
internally in base SI units. For example, calculating the <em>floor</em> of
1 [mm] would evaluate to zero since the floor would be calculated on 0.001 after
the millimeter value is converted to meters for the internal calculation.
</p>

<br>

<em>September 30, 2023</em>
<h4>Improved Error Messages</h4>
<p>
Expand Down
12 changes: 6 additions & 6 deletions src/keyboard/Keyboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -494,8 +494,8 @@ export const keyboards: Keyboards = {
new Button({ buttonText: '\\mathrm{real}', content: '\\mathrm{real}\\left(#0\\right)', command: "insert", size: '1.2fr' }),
new Button({ buttonText: '\\left|x\\right|', content: '\\left|#0\\right|', command: "insert" }),
new Blank('0.1fr'),
new Blank('1fr'),
new Button({ buttonText: '⌫', command: 'deleteBackward' }),
new Button({ buttonText: '\\mathrm{ceil}', content: '\\mathrm{ceil}\\left(#0\\right)', command: "insert"}),
new Button({ buttonText: '⌫', command: 'deleteBackward', size: '1.2fr'}),
],
[
new Button({ buttonText: '\\cos', content: '\\cos\\left(#0\\right)', command: "insert" }),
Expand All @@ -506,8 +506,8 @@ export const keyboards: Keyboards = {
new Button({ buttonText: '\\mathrm{imag}', content: '\\mathrm{imag}\\left(#0\\right)', command: "insert", size: '1.2fr' }),
new Button({ buttonText: '\\mathrm{max}', content: '\\mathrm{max}\\left(#0\\right)', command: "insert" }),
new Blank('0.1fr'),
new Button({ buttonText: '\\leftarrow', command: 'moveToPreviousChar' }),
new Button({ buttonText: '\\rightarrow', command: 'moveToNextChar' }),
new Button({ buttonText: '\\mathrm{floor}', content: '\\mathrm{floor}\\left(#0\\right)', command: "insert"}),
new Button({ buttonText: '\\mathrm{round}', content: '\\mathrm{round}\\left(#0\\right)', command: "insert", size: '1.2fr'}),
],
[
new Button({ buttonText: '\\tan', content: '\\tan\\left(#0\\right)', command: "insert" }),
Expand All @@ -519,7 +519,7 @@ export const keyboards: Keyboards = {
new Button({ buttonText: '\\mathrm{min}', content: '\\mathrm{min}\\left(#0\\right)', command: "insert" }),
new Blank('0.1fr'),
new Button({ buttonText: "\\int", content: '\\int \\left(#0\\right)\\mathrm{d}\\left(#?\\right)', command: "insert", fontSize: '10pt' }),
new Button({ buttonText: '\\int_a^b', content: '\\int _{#?}^{#?}\\left(#0\\right)\\mathrm{d}\\left(#?\\right)', command: "insert", fontSize: '10pt' }),
new Button({ buttonText: '\\int_a^b', content: '\\int _{#?}^{#?}\\left(#0\\right)\\mathrm{d}\\left(#?\\right)', command: "insert", fontSize: '10pt', size: '1.2fr'}),
],
[
new Button({ buttonText: '\\ln', content: '\\ln\\left(#0\\right)', command: "insert" }),
Expand All @@ -531,7 +531,7 @@ export const keyboards: Keyboards = {
new Blank(),
new Blank('0.1fr'),
new Button({ buttonText: "x^{\\prime}", content: '\\frac{\\mathrm{d}}{\\mathrm{d}\\left(#?\\right)}\\left(#0\\right)', command: "insert", fontSize: '12pt' }),
new Button({ buttonText: "x^{\\prime \\prime}", content: '\\frac{\\mathrm{d}^{2}}{\\mathrm{d}\\left(#?\\right)^{2}}\\left(#0\\right)', command: "insert", fontSize: '12pt' })
new Button({ buttonText: "x^{\\prime \\prime}", content: '\\frac{\\mathrm{d}^{2}}{\\mathrm{d}\\left(#?\\right)^{2}}\\left(#0\\right)', command: "insert", fontSize: '12pt', size: '1.2fr'})
]]
}
},
Expand Down
5 changes: 4 additions & 1 deletion src/parser/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ export const BUILTIN_FUNCTION_MAP = new Map([
['det', '_Determinant'],
['transpose', '_Transpose'],
['norm', '_norm'],
['dot', '_dot']
['dot', '_dot'],
['floor', '_floor'],
['ceil', '_ceil'],
['round', '_round']
]);

export const COMPARISON_MAP = new Map([
Expand Down
108 changes: 108 additions & 0 deletions tests/test_functions.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,112 @@ test('Test min/max functions', async ({ browserName }) => {
content = await page.locator('#result-units-12').textContent();
expect(content).toBe('N');

});


test('Test ceil func', async ({ browserName }) => {

await page.locator('#cell-0 >> math-field.editable').type('ceil(1.1)=');

await page.locator('#add-math-cell').click();
await page.locator('#cell-1 >> math-field.editable').type('ceil(1.6)=');

await page.locator('#add-math-cell').click();
await page.locator('#cell-2 >> math-field.editable').type('ceil(2.00000)=');

await page.locator('#add-math-cell').click();
await page.locator('#cell-3 >> math-field.editable').type('ceil(-2.6)=');

await page.locator('#add-math-cell').click();
await page.locator('#cell-4 >> math-field.editable').type('ceil(1[m])=');

await page.waitForSelector('.status-footer', { state: 'detached'});

let content = await page.textContent('#result-value-0');
expect(content).toBe('2');

content = await page.textContent('#result-value-1');
expect(content).toBe('2');

content = await page.textContent('#result-value-2');
expect(content).toBe('2');

content = await page.textContent('#result-value-3');
expect(content).toBe('-2');

await expect(page.locator('#cell-4 >> text=Dimension Error')).toBeAttached();
});


test('Test floor func', async ({ browserName }) => {

await page.locator('#cell-0 >> math-field.editable').type('floor(1.1)=');

await page.locator('#add-math-cell').click();
await page.locator('#cell-1 >> math-field.editable').type('floor(1.6)=');

await page.locator('#add-math-cell').click();
await page.locator('#cell-2 >> math-field.editable').type('floor(1.00000)=');

await page.locator('#add-math-cell').click();
await page.locator('#cell-3 >> math-field.editable').type('floor(-0.9)=');

await page.locator('#add-math-cell').click();
await page.locator('#cell-4 >> math-field.editable').type('floor(1[m])=');

await page.waitForSelector('.status-footer', { state: 'detached'});

let content = await page.textContent('#result-value-0');
expect(content).toBe('1');

content = await page.textContent('#result-value-1');
expect(content).toBe('1');

content = await page.textContent('#result-value-2');
expect(content).toBe('1');

content = await page.textContent('#result-value-3');
expect(content).toBe('-1');

await expect(page.locator('#cell-4 >> text=Dimension Error')).toBeAttached();
});


test('Test round func', async ({ browserName }) => {

await page.locator('#cell-0 >> math-field.editable').type('round(1.1)=');

await page.locator('#add-math-cell').click();
await page.locator('#cell-1 >> math-field.editable').type('round(1.6)=');

await page.locator('#add-math-cell').click();
await page.locator('#cell-2 >> math-field.editable').type('round(1.00000)=');

await page.locator('#add-math-cell').click();
await page.locator('#cell-3 >> math-field.editable').type('round(-0.9)=');

await page.locator('#add-math-cell').click();
await page.locator('#cell-4 >> math-field.editable').type('round(-1.1)=');

await page.locator('#add-math-cell').click();
await page.locator('#cell-5 >> math-field.editable').type('round(1[m])=');

await page.waitForSelector('.status-footer', { state: 'detached'});

let content = await page.textContent('#result-value-0');
expect(content).toBe('1');

content = await page.textContent('#result-value-1');
expect(content).toBe('2');

content = await page.textContent('#result-value-2');
expect(content).toBe('1');

content = await page.textContent('#result-value-3');
expect(content).toBe('-1');

content = await page.textContent('#result-value-4');
expect(content).toBe('-1');

await expect(page.locator('#cell-5 >> text=Dimension Error')).toBeAttached();
});

0 comments on commit dad7e9c

Please sign in to comment.