mathjs icon indicating copy to clipboard operation
mathjs copied to clipboard

Implement support for more than 2 arguments for logical functions `and`, `or`, and `xor`

Open gwhitney opened this issue 2 years ago • 9 comments

As and, or, and xor are associative and commutative, in function form there's no reason they could not take multiple arguments like their arithmetic counterparts add and multiply.

gwhitney avatar Mar 15 '22 23:03 gwhitney

Interesting idea. Yeah, makes sense. I guess that could be very interesting for example for function simplify if that would be capable to deal with a and b and c. I'm not sure if people would actually enter something like and(a, b, c) as a function call over the and operator.

Do you have some concrete use cases where this can prove valuable?

josdejong avatar Mar 16 '22 08:03 josdejong

Well, first there's the consistency with add and multiply (I was surprised when add and or did not work this way) and simply the principle to be as general as is feasible. Second, if you have a large conjunction or disjunction, and(A,B,C,D) is more compact and potentially clearer than A and B and C and D and means you don't have to worry whether/where you need parentheses in and(A or B, C or D, E or F) or or(A and B, C and D, E and F) And finally, I agree: simplify and/or simplifyCore could/ought to handle things like A and false -> A and B or not B -> true and just as with + and *, detecting these patterns in the midst of intervening operands is most feasible if they can be flattened. That's all I can think of at the moment, but I also don't see any harm in this generalization.

gwhitney avatar Mar 16 '22 17:03 gwhitney

Makes sense: consistency, and large sets of values. I'm convinced 😄 .

I'll change the title of this issue to reflect the plans.

josdejong avatar Mar 17 '22 16:03 josdejong

Having add, multiply, and, or take arbitrary arguments is very reminiscent of Scheme. It may be worth considering going all the way with it, see:

https://www.gnu.org/software/mit-scheme/documentation/stable/mit-scheme-ref.pdf

This would also include unary and nullary versions. Granted, these would be of limited use for now, unless somebody is machine generating expressions on the fly. Perhaps in the future there could be some kind of argument packing/unpacking in the style of JavaScript's rest parameters/spread syntax.

Anyhow, that's just some food for thought.

filonik avatar Jun 16 '22 01:06 filonik

Thanks for your inputs Daniel 👍

josdejong avatar Jun 17 '22 07:06 josdejong

Thanks to the excellent customizability of mathjs, it is not too difficult to replace the builtin functions with n-ary versions:

import { create, evaluateDependencies, factory, typed } from 'mathjs'

// See: mathjs/src/plain/number/logical.js

function andNumber(x, y) {
  return !!(x && y)
}

function orNumber(x, y) {
  return !!(x || y)
}

function xorNumber(x, y) {
  return !!x !== !!y
}

const math = create({
  evaluateDependencies,
  createAdd: factory('add', ['addScalar'], ({ addScalar }) =>
    typed('add', {
      '': () => 0,
      '...': (xs) => xs.reduce((x, y) => addScalar(x, y)),
    }),
  ),
  createMultiply: factory('multiply', ['multiplyScalar'], ({ multiplyScalar }) =>
    typed('multiply', {
      '': () => 1,
      '...': (xs) => xs.reduce((x, y) => multiplyScalar(x, y)),
    }),
  ),
  createAnd: factory('and', [], () =>
    typed('and', {
      '': () => true,
      '...': (xs) => xs.reduce(andNumber),
    }),
  ),
  createOr: factory('or', [], () =>
    typed('or', {
      '': () => false,
      '...': (xs) => xs.reduce(orNumber),
    }),
  ),
  createXor: factory('xor', [], () =>
    typed('xor', {
      '': () => false,
      '...': (xs) => xs.reduce(xorNumber),
    }),
  ),
})

export default math

Just sharing in case anyone wants to use these instead of waiting for the library to be updated. Note that these would need to be extended to also work with arrays/matrices (see here).

Btw.: I copied andNumber, orNumber, xorNumber from the mathjs source, but are they exported as part of the public API?

filonik avatar Jul 03 '22 15:07 filonik

Thanks for sharing @filonik 👍

Btw.: I copied andNumber, orNumber, xorNumber from the mathjs source, but are they exported as part of the public API?

They are not exported themselves, but their factory functions createAnd, createOr, etc are exported via mathjs/number.

josdejong avatar Jul 04 '22 08:07 josdejong

Hello @josdejong . It's my first contribution to Open Source so I'm working to implement this and I have a few questions: 1.- It's correct to extend the functionality in the same file to the two arguments? and.js, or.js and xor.js. I have it done here but I don't know if it's 100% right. 2.- I have troubles to make the tests with 3 parameters. Complex, units, bignumbers... maybe can be a lot of tests. It's all right if I upload some samples and take a look if it's all fine?

Thanks

psirdev avatar Feb 05 '23 22:02 psirdev

Thanks @psirdev for picking this up!

  1. I think best is to extend the original functions with a new signature, i.e. the and.js. You can probably do the same as in add.js, add a signature like 'any, any, ...any' and loop over the rest parameters, applying the function itself.
  2. I think the new unit tests can focus on checking that the functions accept more than two arguments, and with mixed data types, but you do not have to test all possible combinations of all data types.

josdejong avatar Feb 06 '23 09:02 josdejong