Dmitrii Lobanov
Dmitrii Lobanov

Reputation: 4977

Is there generic MultiplyByInt?

There's a generic function LanguagePrimitives.DivideByInt to divide by int without losing generic behavior, we can use it like this:

let inline Divideby2 n = LanguagePrimitives.DivideByInt n 2

val inline Divideby2 :
    ^a ->  ^a when  ^a : (static member DivideByInt :  ^a * int ->  ^a)

But there's no function called MultiplyByInt to perform generic multiplication by int. Is there anything to perform generic multiplication? Like this:

let inline MultiplyBy2 n = SomeGenericFunctionsModule.MultiplybyInt n 2;

P.S. we can always use some non-standard approach like:

let inline MultiplyByInt n m = seq { for i in 1..m -> n} |> Seq.sum

but I'm interested if it is possible to do in the right way.

Upvotes: 2

Views: 162

Answers (4)

Dmitrii Lobanov
Dmitrii Lobanov

Reputation: 4977

I've received an answer from Don Syme (via fsbugs email) when I've asked about missing MutliplyByInt and limited support of DivideByInt:

Don's answer:

This operator exists to support “Seq.averageBy” etc. This represents pseudo-precise division of the total by the count. We didn’t extend the mechanism beyond what was needed for that.

So it looks like I've misunderstood the purpose of this mechanism.

Upvotes: 1

Gus
Gus

Reputation: 26174

I'm afraid there's no built-in function, but I can propose two alternative solutions:

type MulExtension = MulExtension of int with
    static member (=>) (x:float  , MulExtension y) = x * (float y)
    static member (=>) (x:decimal, MulExtension y) = x * (decimal y)
    static member (=>) (x:int64  , MulExtension y) = x * (int64 y)
    // More overloads

let inline MultiplyByInt x y = x => MulExtension y

But you'll have to specify each type. I would rather use this function:

let inline MultiplyByInt  x y = 
    let fromInt (n:int) : ^a when  ^a : (static member (*) : ^a * ^a -> ^a) =
        System.Convert.ChangeType(n, typeof<'a>) :?> 'a
    x * (fromInt y)

I can't see any difference in performance between both methods.

Upvotes: 5

Daniel
Daniel

Reputation: 47904

let inline MultiplyByInt n (x: ^a) =
  let zero : ^a = LanguagePrimitives.GenericZero
  let one : ^a = LanguagePrimitives.GenericOne
  if n = 0 then zero
  else
    let mutable q, r = System.Math.DivRem(abs n, 2)
    let mutable y = x
    while q > 0 do
      y <- y + y
      q <- q / 2
    let y = if r = 0 then y else y + x
    if n > 0 then y
    else y * (zero - one)

Upvotes: 1

John Palmer
John Palmer

Reputation: 25516

I managed to get a solution in O(log(N)) which beats yours, but it still feels very ugly

let inline MulInt (m:^t) (n:int) =
    let r : ^t ref = ref LanguagePrimitives.GenericZero
    let addv : ^t ref= ref LanguagePrimitives.GenericOne
    while ((int) !r) < n do
        if int(!r + !addv + !addv) < n then 
            addv := !addv + !addv
        else 
            r := !r + !addv
            addv := LanguagePrimitives.GenericOne

    !r * m

Using some of the library only features could make this a little better, but would result in warnings.

Note: This solution assumes that n is representable in ^t - i.e.

MulInt 2uy 5000

will loop forever

Upvotes: 2

Related Questions