My Fraction Class

Hi guys! I have been working on a fraction class for a while. The usage is documented inside the project.


-- My fraction class

function setup()
    -- Usage of the fraction class
    half = fraction(1, 2) -- Traditional syntax, works well with all fractions, including decimals 
    third = fraction(1, 3)
    quarter = fraction(1 / 4) -- Alternative syntax, does not work for fractions such as 1/3
    fifth = fraction(.2) -- note how the denominator defaults to 1
    pi = fraction(355,113)
    print("Half =", half)
    print("Third =", third, "which in decimal is =", third.value)
    print("Two Quarters =", quarter * 2)
    print("Half a fifth =", half * fifth)
    print("Fifth =", fifth)
    print("A Third plus a half =", third + half )
    print("A half minus a third =", half - third)
    print("One and a half =", half + 1)
    print("Half minus one =", half - 1)
    print("Half didvided by a third =", half/third)
    print("Half to the power of 3 =", half ^ 3)
    print("Half to the power of minus 3 =", half ^ -3)
    print("Negative half =", -half)
    print("My fraction approximation of Pi =", pi.value, "and Codea/Luas approximation =", math.pi)
    print("Enjoy!")
end

fraction = class()

function math.hcf(n, d)
    while not (d == 0) do
        n, d = d, n % d
    end
    return n
end

function fraction:init(n, d)
    self.n = n
    self.d = d or 1
    self:_set()
    self.value = self.n / self.d
end

function fraction:_set()
    if not (self.n % 1 == 0) then
        local zeros = 0
        local rem = .5
        while not (rem % 1 == 0) do
            zeros = zeros + 1
            rem = self.n * (10 ^ zeros)
        end
        self.n = rem
        self.d = self.d * (10 ^ zeros)
    end
    if not (self.d % 1 == 0) then
        local zeros = 0
        local rem = 0.5
        while not (rem % 1 == 0) do
            zeros = zeros + 1
            rem = self.d * (10 ^ zeros)
        end
        self.n = self.n * (10 ^ zeros)
        self.d = rem
    end
    local gcd = math.hcf(self.n, self.d)
    self.n = self.n / gcd
    self.d = self.d / gcd
end

function fraction:__add(t)
    if type(t) == "number" then
        return fraction(self.n + self.d * t, self.d)
    elseif type(t) == "table" and t.n and t.d then
        return fraction(self.n * t.d + t.n * self.d, self.d * t.d)
    end
end

function fraction:__sub(t)
    if type(t) == "number" then
        return fraction(self.n - self.d * t, self.d)
    elseif type(t) == "table" and t.n and t.d then
        return fraction(self.n * t.d - t.n * self.d, self.d * t.d)
    end
end

function fraction:__mul(t)
    if type(t) == "number" then
        return fraction(self.n * t, self.d)
    elseif type(t) == "table" and t.n and t.d then
        return fraction(self.n * t.n, self.d * t.d)
    end
end

function fraction:__div(t)
    if type(t) == "number" then
        return fraction(self.n, self.d * t)
    elseif type(t) == "table" and t.n and t.d then
        return fraction(self.n * t.d, self.d * t.n)
    end
end

function fraction:__unm()
    return fraction(-self.n, self.d)
end

function fraction:__pow(exp)
    local n = fraction(1)
    if exp > 0 then
        for i = 1, exp do
             n = n * fraction(self.n, self.d)
        end
    elseif exp < 0 then
        for i = -1, exp, -1 do
            n = n / fraction(self.n, self.d)
        end
    end
    return n
end

function fraction:__tostring()
    return self.n.."/"..self.d
end

Thanks!

Might want to verify that pow() is passed an integer.

Thanks @SciQuest, I am working on an improved version of this, because all the operators only work with the fraction to the left, I will post my code in a day or two.

Here is my new and improved code! :smiley: It supports Boolean testing, Concatenation, and all operators excluding ^ and %.

-- Fraction class by Jordan Arenstein

fraction = class()

--[[
FEATURES =
Automatically simplifies
Automatically converts floats to integers without changing the value of the fraction
Supports the +, -, *, /, and .. operators
Supports print()
Supports boolean testing using ==, <, >, <=, >= operators

CUTBACKS =
Doesn't support ^ and % operators... yet
Boolean testing only works when comparing fractions

]]--

function setup()
    half = fraction(1, 2)
    print("One half = "..half)
    print("Is one half(fraction(1, 2)) equal to fraction(1/2)? " .. ((half == fraction(1 / 2)) and "true" or "false"))
    third = fraction(1, 3)
    print("A third plus a half = ".. (third + half))
    print("A third minus a half = ".. (third - half))
    print("A third times a half = ".. (third * half))
    print("A third divided by a half = ".. (third / half))
    quarter = half * half
    print("A third("..third..") in decimal = ".. third:decimal())
    print("A quarter is less than a half = ".. ((quarter < half) and "true" or "false"))
    print("1 and a half = ".. (1 + half))
    print("1 minus a third = ".. (1 - third))
    print("5 halves = ".. (5 * half))
    print("2 / half = ".. (2 / half))
    print("Half / 2 = ".. (half / 2))
    print("Codea's representation of Pi = ".. math.pi)
    Pi = fraction(355, 113)
    print("My representation of Pi = ".. Pi:decimal())
end

function math.hcf(n, d) -- Function to find the highest common factor between 2 numbers
    while d ~= 0 do
        n, d = d, n % d
    end
    return n
end

function fraction:init(n, d)
    d = d or 1 -- Default the denominator to 1 so the syntax 1/2 works
    if n % 1 ~= 0 then -- If the numerator isn't whole then...
        local z = 0 -- The number of zeros needed to times the numerator by until it is a whole number
        local r = 0.1 -- The remainder
        while r % 1 ~= 0 do
            z = z + 1
            r = n * (10 ^ z) -- 10 to the power of z
        end
        n = r -- Make n a whole number
        d = d * (10 ^ z) -- Multiply the denominator too, so the ratio stays the same
    end
    -- Same proccess for the denominator
    if d % 1 ~= 0 then
        local z = 0
        local r = 0.1
        while r % 1 ~= 0 do
            z = z + 1
            r = d * (10 ^ z)
        end
        n = n * (10 ^ z)
        d = r
    end
    local gcd = math.hcf(n, d)
    self.n = n / gcd
    self.d = d / gcd
end

function fraction:decimal()
    return self.n / self.d
end

function fraction:__add(n)
    if type(n) == "number" then -- If the value to the right of the operator is a number (fract + 2)
        return fraction(self.n + self.d * n, self.d)
    end
    if type(self) == "number" then -- If the value to the left is a number (2 + fract)
        return fraction(n.n + n.d * self, n.d)
    end
    -- Then both must be fractions!
    return fraction(self.n * n.d + n.n * self.d, self.d * n.d)
end

function fraction:__sub(n)
    if type(n) == "number" then -- If the value to the right of the operator is a number (fract - 2)
        return fraction(self.n - self.d * n, self.d)
    end
    if type(self) == "number" then -- If the value to the left is a number (2 - fract)
        return fraction(n.d * self - n.n, n.d)
    end
    -- Then both must be fractions!
    return fraction(self.n * n.d - n.n * self.d, self.d * n.d)
end

function fraction:__mul(n)
    if type(n) == "number" then -- If the value to the right of the operator is a number (fract * 2)
        return fraction(self.n * n, self.d)
    end
    if type(self) == "number" then -- If the value to the left is a number (2 * fract)
        return fraction(n.n * self, n.d)
    end
    -- Then both must be fractions!
    return fraction(self.n * n.n, self.d * n.d)
end

function fraction:__div(n)
    if type(n) == "number" then -- If the value to the right of the operator is a number (fract / 2)
        return fraction(self.n, self.d * n)
    end
    if type(self) == "number" then -- If the value to the left is a number (2 / fract)
        return fraction(n.n * self, n.d)
    end
    -- Then both must be fractions!
    return fraction(self.n * n.d, self.d * n.n)
end

function fraction:__unm()
    return fraction(-self.n, self.d)
end

--[[ Removing this one, as it is extremely buggy. See:
(http://twolivesleft.com/Codea/Talk/discussion/1538/modulo-headache)
function fraction:__mod(n)
    -- The formula for modulo = a % b == a - math.floor(a/b)*b
    if type(n) == "number" then
        return self - math.floor((self / n):decimal()) * n
    end
    if type(self) == "number" then
        return self - math.floor((self / n):decimal()) * n
    end
    return fraction(self.n * n.d, self.d * n.n)
end
]]--

function fraction:__concat(str)
    if type(self) == "table" and self.n and self.d then -- It has been called in the format fract .. str
        return self:__tostring() .. str
    elseif type(str) == "table" and str.n and str.d then -- It has been called in the format str .. fract
        return self .. str:__tostring()
    end
end

function fraction:__tostring()
    return self.n .. "/" .. self.d
end

function fraction:__eq(n)
    if self.d == n.d and self.n == n.n then
        return true
    end
    return false
end

function fraction:__lt(n) -- The < operator
    return self.n * n.d < n.n * self.d
end

function fraction:__le(n) -- The <= operator
    return self.n * n.d <= n.n * self.d
end

function fraction:__gt(n) -- The < operator
    return self.n * n.d > n.n * self.d
end

function fraction:__ge(n) -- The <= operator
    return self.n * n.d >= n.n * self.d
end