Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
379 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
local common = require "dial.augend.common" | ||
local util = require "dial.util" | ||
|
||
---@class AugendDecimalFraction | ||
---@implement Augend | ||
---@field signed boolean | ||
---@field point_char string | ||
local AugendDecimalFraction = {} | ||
|
||
local M = {} | ||
|
||
---@param config { signed?: boolean, point_char?: string } | ||
---@return Augend | ||
function M.new(config) | ||
vim.validate { | ||
signed = { config.signed, "boolean", true }, | ||
point_char = { config.point_char, "string", true }, | ||
} | ||
|
||
local signed = util.unwrap_or(config.signed, false) | ||
local point_char = util.unwrap_or(config.point_char, ".") | ||
local digits_to_add = 0 | ||
|
||
return setmetatable({ | ||
signed = signed, | ||
point_char = point_char, | ||
digits_to_add = digits_to_add, | ||
}, { __index = AugendDecimalFraction }) | ||
end | ||
|
||
---@param line string | ||
---@param cursor? integer | ||
---@return textrange? | ||
function AugendDecimalFraction:find(line, cursor) | ||
local idx = 1 | ||
local integer_pattern | ||
if self.signed then | ||
integer_pattern = "%-?%d+" | ||
else | ||
integer_pattern = "%d+" | ||
end | ||
while idx <= #line do | ||
local idx_integer_start, idx_integer_end = line:find(integer_pattern, idx) | ||
if idx_integer_start == nil then | ||
break | ||
end | ||
|
||
local result = (function() | ||
idx = idx_integer_end + 1 | ||
-- invalid decimal fraction format | ||
if line:sub(idx, idx) ~= self.point_char then | ||
return -- continue while loop | ||
end | ||
idx = idx + 1 | ||
local idx_frac_start, idx_frac_end = line:find("^%d+", idx) | ||
-- invalid decimal fraction format | ||
if idx_frac_start == nil then | ||
return -- continue while loop | ||
end | ||
idx = idx_frac_end + 1 | ||
-- decimal fraction before the cursor | ||
if idx_frac_end < cursor then | ||
return -- continue while loop | ||
end | ||
|
||
-- negative lookahead | ||
if line:sub(idx, idx) == self.point_char then | ||
return -- continue while loop | ||
end | ||
-- break loop and return value | ||
return { from = idx_integer_start, to = idx_frac_end } | ||
end)() | ||
|
||
if result ~= nil then | ||
return result | ||
end | ||
end | ||
end | ||
|
||
---@param line string | ||
---@param cursor? integer | ||
---@return textrange? | ||
function AugendDecimalFraction:find_stateful(line, cursor) | ||
local result = self:find(line, cursor) | ||
if result == nil then | ||
return nil | ||
end | ||
|
||
local point_pos = line:find(self.point_char, result.from, true) | ||
if cursor < point_pos then | ||
-- increment integer part | ||
self.digits_to_add = 0 | ||
else | ||
-- increment decimal part | ||
self.digits_to_add = result.to - point_pos | ||
end | ||
return result | ||
end | ||
|
||
---@param text string | ||
---@param addend integer | ||
---@param cursor? integer | ||
---@return { text?: string, cursor?: integer } | ||
function AugendDecimalFraction:add(text, addend, cursor) | ||
local point_pos = text:find(self.point_char, 1, true) | ||
|
||
local int_part = text:sub(1, point_pos - 1) | ||
local frac_part = text:sub(point_pos + 1) | ||
|
||
-- 桁数調整。元の数字が 12.3 なのに 0.01 を足したいとき、 12.31 になるようにする | ||
if #frac_part < self.digits_to_add then | ||
frac_part = frac_part .. ("0"):rep(self.digits_to_add - #frac_part) | ||
end | ||
|
||
local num = tonumber(int_part .. frac_part) | ||
local add_num = addend * math.floor(10 ^ (#frac_part - self.digits_to_add)) | ||
num = num + add_num | ||
if not self.signed and num < 0 then | ||
num = 0 | ||
end | ||
local str_num = tostring(num) | ||
|
||
if num < 0 then | ||
if #str_num - 1 <= #frac_part then | ||
str_num = "-" .. ("0"):rep(#frac_part + 2 - #str_num) .. str_num:sub(2) | ||
end | ||
else | ||
if #str_num <= #frac_part then | ||
str_num = ("0"):rep(#frac_part + 1 - #str_num) .. str_num | ||
end | ||
end | ||
|
||
-- pad as necessary | ||
local new_int_part = str_num:sub(1, #str_num - #frac_part) | ||
local new_dec_part = str_num:sub(#str_num - #frac_part + 1) | ||
|
||
text = new_int_part .. "." .. new_dec_part | ||
if self.digits_to_add == 0 then | ||
-- incremented integer part | ||
cursor = #new_int_part | ||
else | ||
cursor = #text | ||
end | ||
|
||
return { text = text, cursor = cursor } | ||
end | ||
|
||
M.alias = {} | ||
|
||
return M |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
local decimal_fraction = require("dial.augend").decimal_fraction | ||
|
||
describe("Test of decimal fraction { signed = false }", function() | ||
local augend = decimal_fraction.new { signed = false } | ||
|
||
describe("find function", function() | ||
it("can find a decimal fraction", function() | ||
assert.are.same(augend:find_stateful("273.15", 1), { from = 1, to = 6 }) | ||
assert.are.same(augend.digits_to_add, 0) | ||
assert.are.same(augend:find_stateful("0.0", 1), { from = 1, to = 3 }) | ||
assert.are.same(augend.digits_to_add, 0) | ||
assert.are.same(augend:find_stateful("123 273.15", 1), { from = 5, to = 10 }) | ||
assert.are.same(augend.digits_to_add, 0) | ||
assert.are.same(augend:find_stateful("foo 42.195 28.3 bar", 1), { from = 5, to = 10 }) | ||
assert.are.same(augend.digits_to_add, 0) | ||
assert.are.same(augend:find_stateful("foo 42.195 28.3 bar", 5), { from = 5, to = 10 }) | ||
assert.are.same(augend.digits_to_add, 0) | ||
assert.are.same(augend:find_stateful("foo 42.195 28.3 bar", 7), { from = 5, to = 10 }) | ||
assert.are.same(augend.digits_to_add, 3) | ||
assert.are.same(augend:find_stateful("foo 42.195 28.3 bar", 10), { from = 5, to = 10 }) | ||
assert.are.same(augend.digits_to_add, 3) | ||
assert.are.same(augend:find_stateful("foo 42.195 28.3 bar", 11), { from = 12, to = 15 }) | ||
assert.are.same(augend.digits_to_add, 0) | ||
assert.are.same(augend:find_stateful("foo 42.195 28.3 bar", 14), { from = 12, to = 15 }) | ||
assert.are.same(augend.digits_to_add, 1) | ||
assert.are.same(augend:find_stateful("foo 42.195 28.3 bar", 16), nil) | ||
end) | ||
|
||
it("changes the value of digits_to_add appropriately depending on the cursor position", function() | ||
assert.are.same(augend:find_stateful("273.15", 1), { from = 1, to = 6 }) | ||
assert.are.same(augend.digits_to_add, 0) | ||
assert.are.same(augend:find_stateful("273.15", 3), { from = 1, to = 6 }) | ||
assert.are.same(augend.digits_to_add, 0) | ||
assert.are.same(augend:find_stateful("273.15", 4), { from = 1, to = 6 }) | ||
assert.are.same(augend.digits_to_add, 2) | ||
assert.are.same(augend:find_stateful("273.15", 5), { from = 1, to = 6 }) | ||
assert.are.same(augend.digits_to_add, 2) | ||
assert.are.same(augend:find_stateful("273.15", 6), { from = 1, to = 6 }) | ||
assert.are.same(augend.digits_to_add, 2) | ||
|
||
assert.are.same(augend:find_stateful("9.80665", 1), { from = 1, to = 7 }) | ||
assert.are.same(augend.digits_to_add, 0) | ||
assert.are.same(augend:find_stateful("9.80665", 3), { from = 1, to = 7 }) | ||
assert.are.same(augend.digits_to_add, 5) | ||
assert.are.same(augend:find_stateful("9.80665", 4), { from = 1, to = 7 }) | ||
assert.are.same(augend.digits_to_add, 5) | ||
assert.are.same(augend:find_stateful("9.80665", 5), { from = 1, to = 7 }) | ||
assert.are.same(augend.digits_to_add, 5) | ||
assert.are.same(augend:find_stateful("9.80665", 6), { from = 1, to = 7 }) | ||
assert.are.same(augend.digits_to_add, 5) | ||
end) | ||
|
||
it("returns nil when no number appears", function() | ||
assert.are.same(augend:find("", 1), nil) | ||
assert.are.same(augend:find("foo bar", 1), nil) | ||
end) | ||
it("doesn't pick up negative number", function() | ||
assert.are.same(augend:find("-42.195", 1), { from = 2, to = 7 }) | ||
assert.are.same(augend:find("fname-1.3", 1), { from = 7, to = 9 }) | ||
end) | ||
it("doesn't pick up incomplete decimal fraction", function() | ||
assert.are.same(augend:find("-42.", 1), nil) | ||
assert.are.same(augend:find("fname-1.png", 1), nil) | ||
assert.are.same(augend:find(".1", 1), nil) | ||
end) | ||
end) | ||
|
||
describe("add function", function() | ||
it("can increment integer parts", function() | ||
augend.digits_to_add = 0 | ||
assert.are.same(augend:add("273.15", 1, 1), { text = "274.15", cursor = 3 }) | ||
assert.are.same(augend:add("273.15", 10, 1), { text = "283.15", cursor = 3 }) | ||
assert.are.same(augend:add("273.15", 10, 4), { text = "283.15", cursor = 3 }) | ||
assert.are.same(augend:add("273.15", 100, 1), { text = "373.15", cursor = 3 }) | ||
assert.are.same(augend:add("273.15", 1000, 1), { text = "1273.15", cursor = 4 }) | ||
assert.are.same(augend:add("273.15", -1, 1), { text = "272.15", cursor = 3 }) | ||
assert.are.same(augend:add("273.15", -10, 1), { text = "263.15", cursor = 3 }) | ||
assert.are.same(augend:add("273.15", -100, 1), { text = "173.15", cursor = 3 }) | ||
assert.are.same(augend:add("273.15", -200, 1), { text = "73.15", cursor = 2 }) | ||
assert.are.same(augend:add("273.15", -270, 1), { text = "3.15", cursor = 1 }) | ||
assert.are.same(augend:add("273.15", -273, 1), { text = "0.15", cursor = 1 }) | ||
assert.are.same(augend:add("273.15", -274, 1), { text = "0.00", cursor = 1 }) | ||
assert.are.same(augend:add("273.15", -1000, 1), { text = "0.00", cursor = 1 }) | ||
end) | ||
it("can increment fragment parts", function() | ||
augend.digits_to_add = 1 | ||
assert.are.same(augend:add("273.15", 1, 1), { text = "273.25", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", 9, 1), { text = "274.05", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", 10, 1), { text = "274.15", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", 10, 4), { text = "274.15", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", 100, 1), { text = "283.15", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", 1000, 1), { text = "373.15", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", 10000, 1), { text = "1273.15", cursor = 7 }) | ||
assert.are.same(augend:add("273.15", -1, 1), { text = "273.05", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", -2, 1), { text = "272.95", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", -10, 1), { text = "272.15", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", -100, 1), { text = "263.15", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", -1000, 1), { text = "173.15", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", -10000, 1), { text = "0.00", cursor = 4 }) | ||
|
||
augend.digits_to_add = 2 | ||
assert.are.same(augend:add("273.15", 1, 1), { text = "273.16", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", 5, 1), { text = "273.20", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", 10, 1), { text = "273.25", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", 90, 1), { text = "274.05", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", 1000, 1), { text = "283.15", cursor = 6 }) | ||
|
||
augend.digits_to_add = 3 | ||
assert.are.same(augend:add("273.15", 1, 1), { text = "273.151", cursor = 7 }) | ||
assert.are.same(augend:add("273.15", 5, 1), { text = "273.155", cursor = 7 }) | ||
assert.are.same(augend:add("273.15", 10, 1), { text = "273.160", cursor = 7 }) | ||
end) | ||
end) | ||
end) | ||
|
||
describe("Test of decimal fraction { signed = true }", function() | ||
local augend = decimal_fraction.new { signed = true } | ||
|
||
describe("find function", function() | ||
it("can find a decimal fraction", function() | ||
assert.are.same(augend:find_stateful("273.15", 1), { from = 1, to = 6 }) | ||
assert.are.same(augend.digits_to_add, 0) | ||
assert.are.same(augend:find_stateful("0.0", 1), { from = 1, to = 3 }) | ||
assert.are.same(augend.digits_to_add, 0) | ||
assert.are.same(augend:find_stateful("123 273.15", 1), { from = 5, to = 10 }) | ||
assert.are.same(augend.digits_to_add, 0) | ||
assert.are.same(augend:find_stateful("foo 42.195 28.3 bar", 1), { from = 5, to = 10 }) | ||
assert.are.same(augend.digits_to_add, 0) | ||
assert.are.same(augend:find_stateful("foo 42.195 28.3 bar", 5), { from = 5, to = 10 }) | ||
assert.are.same(augend.digits_to_add, 0) | ||
assert.are.same(augend:find_stateful("foo 42.195 28.3 bar", 7), { from = 5, to = 10 }) | ||
assert.are.same(augend.digits_to_add, 3) | ||
assert.are.same(augend:find_stateful("foo 42.195 28.3 bar", 10), { from = 5, to = 10 }) | ||
assert.are.same(augend.digits_to_add, 3) | ||
assert.are.same(augend:find_stateful("foo 42.195 28.3 bar", 11), { from = 12, to = 15 }) | ||
assert.are.same(augend.digits_to_add, 0) | ||
assert.are.same(augend:find_stateful("foo 42.195 28.3 bar", 14), { from = 12, to = 15 }) | ||
assert.are.same(augend.digits_to_add, 1) | ||
assert.are.same(augend:find_stateful("foo 42.195 28.3 bar", 16), nil) | ||
end) | ||
|
||
it("changes the value of digits_to_add appropriately depending on the cursor position", function() | ||
assert.are.same(augend:find_stateful("273.15", 1), { from = 1, to = 6 }) | ||
assert.are.same(augend.digits_to_add, 0) | ||
assert.are.same(augend:find_stateful("273.15", 3), { from = 1, to = 6 }) | ||
assert.are.same(augend.digits_to_add, 0) | ||
assert.are.same(augend:find_stateful("273.15", 4), { from = 1, to = 6 }) | ||
assert.are.same(augend.digits_to_add, 2) | ||
assert.are.same(augend:find_stateful("273.15", 5), { from = 1, to = 6 }) | ||
assert.are.same(augend.digits_to_add, 2) | ||
assert.are.same(augend:find_stateful("273.15", 6), { from = 1, to = 6 }) | ||
assert.are.same(augend.digits_to_add, 2) | ||
|
||
assert.are.same(augend:find_stateful("9.80665", 1), { from = 1, to = 7 }) | ||
assert.are.same(augend.digits_to_add, 0) | ||
assert.are.same(augend:find_stateful("9.80665", 3), { from = 1, to = 7 }) | ||
assert.are.same(augend.digits_to_add, 5) | ||
assert.are.same(augend:find_stateful("9.80665", 4), { from = 1, to = 7 }) | ||
assert.are.same(augend.digits_to_add, 5) | ||
assert.are.same(augend:find_stateful("9.80665", 5), { from = 1, to = 7 }) | ||
assert.are.same(augend.digits_to_add, 5) | ||
assert.are.same(augend:find_stateful("9.80665", 6), { from = 1, to = 7 }) | ||
assert.are.same(augend.digits_to_add, 5) | ||
end) | ||
|
||
it("returns nil when no number appears", function() | ||
assert.are.same(augend:find("", 1), nil) | ||
assert.are.same(augend:find("foo bar", 1), nil) | ||
end) | ||
it("picks up negative number", function() | ||
assert.are.same(augend:find("-42.195", 1), { from = 1, to = 7 }) | ||
assert.are.same(augend:find("fname-1.3", 1), { from = 6, to = 9 }) | ||
end) | ||
it("doesn't pick up incomplete decimal fraction", function() | ||
assert.are.same(augend:find("-42.", 1), nil) | ||
assert.are.same(augend:find("fname-1.png", 1), nil) | ||
assert.are.same(augend:find(".1", 1), nil) | ||
end) | ||
end) | ||
|
||
describe("add function", function() | ||
it("can increment integer parts", function() | ||
augend.digits_to_add = 0 | ||
assert.are.same(augend:add("273.15", 1, 1), { text = "274.15", cursor = 3 }) | ||
assert.are.same(augend:add("273.15", 10, 1), { text = "283.15", cursor = 3 }) | ||
assert.are.same(augend:add("273.15", 10, 4), { text = "283.15", cursor = 3 }) | ||
assert.are.same(augend:add("273.15", 100, 1), { text = "373.15", cursor = 3 }) | ||
assert.are.same(augend:add("273.15", 1000, 1), { text = "1273.15", cursor = 4 }) | ||
assert.are.same(augend:add("273.15", -1, 1), { text = "272.15", cursor = 3 }) | ||
assert.are.same(augend:add("273.15", -10, 1), { text = "263.15", cursor = 3 }) | ||
assert.are.same(augend:add("273.15", -100, 1), { text = "173.15", cursor = 3 }) | ||
assert.are.same(augend:add("273.15", -200, 1), { text = "73.15", cursor = 2 }) | ||
assert.are.same(augend:add("273.15", -270, 1), { text = "3.15", cursor = 1 }) | ||
assert.are.same(augend:add("273.15", -273, 1), { text = "0.15", cursor = 1 }) | ||
assert.are.same(augend:add("273.15", -274, 1), { text = "-0.85", cursor = 2 }) | ||
assert.are.same(augend:add("273.15", -1000, 1), { text = "-726.85", cursor = 4 }) | ||
end) | ||
it("can increment fragment parts", function() | ||
augend.digits_to_add = 1 | ||
assert.are.same(augend:add("273.15", 1, 1), { text = "273.25", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", 9, 1), { text = "274.05", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", 10, 1), { text = "274.15", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", 10, 4), { text = "274.15", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", 100, 1), { text = "283.15", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", 1000, 1), { text = "373.15", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", 10000, 1), { text = "1273.15", cursor = 7 }) | ||
assert.are.same(augend:add("273.15", -1, 1), { text = "273.05", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", -2, 1), { text = "272.95", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", -10, 1), { text = "272.15", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", -100, 1), { text = "263.15", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", -1000, 1), { text = "173.15", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", -10000, 1), { text = "-726.85", cursor = 7 }) | ||
|
||
augend.digits_to_add = 2 | ||
assert.are.same(augend:add("273.15", 1, 1), { text = "273.16", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", 5, 1), { text = "273.20", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", 10, 1), { text = "273.25", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", 90, 1), { text = "274.05", cursor = 6 }) | ||
assert.are.same(augend:add("273.15", 1000, 1), { text = "283.15", cursor = 6 }) | ||
|
||
augend.digits_to_add = 3 | ||
assert.are.same(augend:add("273.15", 1, 1), { text = "273.151", cursor = 7 }) | ||
assert.are.same(augend:add("273.15", 5, 1), { text = "273.155", cursor = 7 }) | ||
assert.are.same(augend:add("273.15", 10, 1), { text = "273.160", cursor = 7 }) | ||
end) | ||
end) | ||
end) |