From 8899dd87ea006c52ee28346b862afc0e67d0c8aa Mon Sep 17 00:00:00 2001 From: dlesnoff <54949944+dlesnoff@users.noreply.github.com> Date: Fri, 23 Jun 2023 22:16:52 +0200 Subject: [PATCH] Create and populate the Maths directory (#4) * Add a sample of maths basic algorithms * Update maths/abs.nim Co-authored-by: Zoom * Use openArray in absMaxSort Co-authored-by: Zoom * Fix seq->openArray and int->SomeInteger * Use Positive as input instead * Update maths/addition_without_arithmetic.nim Co-authored-by: Pietro Peterlongo * Name allocation number * [maths/abs] Replace maxAbsSort by signed[Min/Max]Abs * [Aliquot Sum] Add header * Add empty line at end of file * Remove Allocation number since it is a non standard algorithm * Fix titles * Run nimpretty on the files * Rename file and add DIRECTORY.md * abs: Fix variable name * Add export marker Co-authored-by: Zoom * Add RE block in aliquot sum and improve RE Co-authored-by: Zoom * Remove MD quotation marks for inline code * Add export marker for RE block * Remove extra ValueError --------- Co-authored-by: Dimitri LESNOFF Co-authored-by: Zoom Co-authored-by: Pietro Peterlongo Co-authored-by: David Leal --- DIRECTORY.md | 5 ++ maths/abs.nim | 120 +++++++++++++++++++++++++++++++++++++ maths/aliquot_sum.nim | 31 ++++++++++ maths/bitwise_addition.nim | 38 ++++++++++++ 4 files changed, 194 insertions(+) create mode 100644 maths/abs.nim create mode 100644 maths/aliquot_sum.nim create mode 100644 maths/bitwise_addition.nim diff --git a/DIRECTORY.md b/DIRECTORY.md index 3f37dde1..a3259a7b 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -6,6 +6,11 @@ * [Catalan Numbers](dynamic_programming/catalan_numbers.nim) * [Viterbi](dynamic_programming/viterbi.nim) +## Maths + * [Absolute value](maths/abs.nim) + * [Aliquot sum](maths/aliquot_sum.nim) + * [Bitwise Addition](maths/bitwise_addition.nim) + ## Strings * [Check Anagram](strings/check_anagram.nim) diff --git a/maths/abs.nim b/maths/abs.nim new file mode 100644 index 00000000..ed111faa --- /dev/null +++ b/maths/abs.nim @@ -0,0 +1,120 @@ +## Absolute value +{.push raises: [].} +import std/strutils + +runnableExamples: + assert absVal(-5.1) == 5.1 + assert absMin(@[-1, 2, -3]) == 1 + assert absMax(@[-1, 2, -3]) == 3 + assert signedMinAbs(@[3, -10, -2]) == -2 + assert signedMaxAbs(@[3, -10, -2]) == -10 + +func absVal*[T: SomeFloat](num: T): T = + ## Returns the absolute value of a number. + ## Use `math.abs `_ instead! + return if num < 0.0: -num else: num + +# Same for Integers but returns a Natural +func absVal*[T: SomeInteger](num: T): Natural = (if num < 0: -num else: num) + +func absMin*(x: openArray[int]): Natural {.raises: [ValueError].} = + ## Returns the smallest element in absolute value in a sequence. + if x.len == 0: + raise newException(ValueError, """Cannot find absolute minimum + of an empty sequence""".unindent) + result = absVal(x[0]) + for i in 1 ..< x.len: + if absVal(x[i]) < result: + result = absVal(x[i]) + +func absMax*(x: openArray[int]): Natural {.raises: [ValueError].} = + ## Returns the largest element in absolute value in a sequence. + if x.len == 0: + raise newException(ValueError, """Cannot find absolute maximum of an empty + sequence""".unindent) + result = absVal(x[0]) + for i in 1 ..< x.len: + if absVal(x[i]) > result: + result = absVal(x[i]) + +func signedMinAbs*(x: openArray[int]): int {.raises: [ValueError].} = + ## Returns the first signed element whose absolute value + ## is the smallest in a sequence. + if x.len == 0: + raise newException(ValueError, """Cannot find absolute maximum of an empty + sequence""".unindent) + var (min, minAbs) = (x[0], absVal(x[0])) + for n in x: + let nAbs = absVal(n) + if nAbs < minAbs: (min, minAbs) = (n, nAbs) + min + +func signedMaxAbs*(x: openArray[int]): int {.raises: [ValueError].} = + ## Returns the first signed element whose absolute value + ## is the largest in a sequence. + if x.len == 0: + raise newException(ValueError, """Cannot find absolute maximum of an empty + sequence""".unindent) + var (max, maxAbs) = (x[0], absVal(x[0])) + for n in x: + let nAbs = absVal(n) + if nAbs > maxAbs: (max, maxAbs) = (n, nAbs) + max + +when isMainModule: + import std/[unittest, random] + randomize() + + suite "Check absVal": + test "Check absVal": + check: + absVal(11.2) == 11.2 + absVal(5) == 5 + absVal(-5.1) == 5.1 + absVal(-5) == absVal(5) + absVal(0) == 0 + + suite "Check absMin": + test "Check absMin": + check: + absMin(@[-1, 2, -3]) == 1 + absMin(@[0, 5, 1, 11]) == 0 + absMin(@[3, -10, -2]) == 2 + absMin([-1, 2, -3]) == 1 + absMin([0, 5, 1, 11]) == 0 + absMin([3, -10, -2]) == 2 + + test "absMin on empty sequence raises ValueError": + doAssertRaises(ValueError): + discard absMin(@[]) + + suite "Check absMax": + test "Check absMax": + check: + absMax(@[0, 5, 1, 11]) == 11 + absMax(@[3, -10, -2]) == 10 + absMax(@[-1, 2, -3]) == 3 + + test "`absMax` on empty sequence raises ValueError": + doAssertRaises(ValueError): + discard absMax(@[]) + + suite "Check signedMinAbs": + test "Check signedMinAbs": + check: + signedMinAbs(@[0, 5, 1, 11]) == 0 + signedMinAbs(@[3, -2, 1, -4, 5, -6]) == 1 + signedMinAbs(@[3, -2, -1, -4, 5, -6]) == -1 + + test "Among two minimal elements, the first one is returned": + check signedMinAbs(@[3, -2, 1, -4, 5, -6, -1]) == 1 + + suite "Check signedMaxAbs": + test "Check signedMaxAbs": + check: + signedMaxAbs(@[3, -2, 1, -4, 5, -6]) == -6 + signedMaxAbs(@[0, 5, 1, 11]) == 11 + + test "signedMaxAbs on empty sequence raises ValueError": + doAssertRaises(ValueError): + discard signedMaxAbs(@[]) diff --git a/maths/aliquot_sum.nim b/maths/aliquot_sum.nim new file mode 100644 index 00000000..711e24d6 --- /dev/null +++ b/maths/aliquot_sum.nim @@ -0,0 +1,31 @@ +## Aliquot sum +## In number theory, the aliquot sum s(n) of a positive integer n is the sum of +## all proper divisors of n, that is, all divisors of n other than n itself. +## https://en.wikipedia.org/wiki/Aliquot_sum + +runnableExamples: + import std/strformat + const expected = [16, 117] + for i, number in [12, 100].pairs(): + let sum = aliquotSum(number) + assert sum == expected[i] + echo fmt"The sum of all the proper divisors of {number} is {sum}" + +func aliquotSum*(number: Positive): Natural = + ## Returns the sum of all the proper divisors of the number + ## Example: aliquotSum(12) = 1 + 2 + 3 + 4 + 6 = 16 + result = 0 + for divisor in 1 .. (number div 2): + if number mod divisor == 0: + result += divisor + +when isMainModule: + import std/unittest + suite "Check aliquotSum": + test "aliquotSum on small values": + var + input = @[1, 2, 9, 12, 27, 100] + expected = @[0, 1, 4, 16, 13, 117] + for i in 0 ..< input.len: + check: + aliquotSum(input[i]) == expected[i] diff --git a/maths/bitwise_addition.nim b/maths/bitwise_addition.nim new file mode 100644 index 00000000..e024f506 --- /dev/null +++ b/maths/bitwise_addition.nim @@ -0,0 +1,38 @@ +## Bitwise Addition +## Illustrate how to implement addition of integers using bitwise operations +## See https://en.wikipedia.org/wiki/Bitwise_operation#Applications +runnableExamples: + import std/strformat + var + a = 5 + b = 6 + echo fmt"The sum of {a} and {b} is {add(a,b)}" + +func add*(first: int, second: int): int = + ## Implementation of addition of integer with `and`, `xor` and `shl` + ## boolean operators. + var first = first + var second = second + while second != 0: + var c = first and second + first = first xor second + second = c shl 1 + return first + +when isMainModule: + import std/unittest + + suite "Check addition": + test "Addition of two positive numbers": + check: + add(3, 5) == 8 + add(13, 5) == 18 + test "Addition of two negative numbers": + check: + add(-7, -2) == -9 + add(-321, -0) == -321 + test "Addition of one positive and one negative number": + check: + add(-7, 2) == -5 + add(-13, 5) == -8 + add(13, -5) == 8