模組:Complex Number

本页使用了标题或全文手工转换
被永久保护的模块
维基百科,自由的百科全书
文档图示 模块文档[查看] [编辑] [历史] [清除缓存]

本模組為Lua定義了一套複數(如虚数四元數)運算的系統,可提供其他模組呼叫使用,而若要直接在模板或條目中使用可透過Module:Complex Number/Calculate‎或{{複變運算}}來完成。

關於本模組創建動機詳見Module:TemplateParameters#設計緣由(亦可參考Template_talk:Root)。

模組內容

本模組有4套數學資料結構的定義以及對應的數學運算庫:

.cmath
複數的數學資料結構及運算的系統
.qmath
四元數的數學資料結構及運算的系統
.math
實數運算系統的擴充
.bmath
布林代數的數學資料結構及運算的系統

使用方法

  1. 初始化數學庫
    • local 自訂函數庫名稱 = require("Module:Complex Number").函數庫名稱.init()
      例如:local cmath = require("Module:Complex Number").cmath.init()
  2. 初始化指定數學結構的數字
    • local 變數名稱 = 自訂函數庫名稱.constructor("描述數字的字串")
      例如:local num1 = cmath.constructor("2+3i")
  3. 執行運算
    例如:
    local num1 = cmath.constructor("2+3i")
    local num2 = cmath.constructor("4+5i")
    print(num1 * num2)
    
    輸出:-7+22i
    或者使用函數庫內容:
    local num1 = cmath.constructor("i")
    print(cmath.sqrt(num1))
    
    輸出:0.70710678118655+0.70710678118655i

原理

複數可分為實部和虛部,此特性可以透過Lua的table功能({real=..., imag=...,})來實現,同時透過複寫Metatables來完成其各運算子(如+-*/)來實現複變的基本運算:

p.ComplexNumberMeta = {
	__add = function (op1, op2) 
		return p.ComplexNumber(op1.real + op2.real, imag = op1.imag + op2.imag)
	end,
	--...
}
function p.ComplexNumber(real, imag)
	local complexNumber = {real = op1.real + op2.real, imag = op1.imag + op2.imag}
	setmetatable(complexNumber,p.ComplexNumberMeta)
	return complexNumber
end

如此一來,只要是設定過Metatables的含實部和虛部的table都可以直接進行複變數的運算。

剩下的部分就是完善數學函數庫math.xxx的各函數。

比較

函數庫 預設的math .cmath .qmath .math .bmath .tagmath
位於Module:Complex Number/Calculate‎
說明 Lua預設提供的math程式庫 複數)專用程式庫 四元數)專用程式庫 預設math的擴充,定義了上方兩個程式庫中的功能 簡單的布林代數 會運算成<math></math>的程式庫
函式庫初始化方式 無須初始化 cmath = require("Module:Complex Number").cmath.init(); qmath = require("Module:Complex Number").qmath.init(); math = require("Module:Complex Number").math.init(); bmath = require("Module:Complex Number").bmath.init(); tagmath = require("Module:Complex Number/Calculate").tagmath.init();
數字建構/初始化方式 tonumber("10");
10
cmath.toComplexNumber("1+i");
cmath.getComplexNumber(1,1);
qmath.toQuaternionNumber("i+j+k");
qmath.getQuaternionNumber(0,1,1,1);
tonumber("10");
10
bmath.toBoolean("yes"); tagmath.toTagMath("a");
四則運算 加法
a + b
是 是 是 lua原生支援 邏輯或 輸出
減法
a - b
是 是 是 lua原生支援 是 輸出
乘法
a * b
是 是 是 lua原生支援 邏輯與 輸出
除法
a / b
是 是 只能除實數 lua原生支援 不存在 輸出
模除
a % b
是 是 以高斯符號定義 lua原生支援 不存在 否
一元運算 相反數
-a
是 是 是 lua原生支援 邏輯非 輸出
tostring 是 是 是 lua原生支援 是 是
e常數
e
否 是 是 是 否 輸出
圓周率
pi
是 是 是 lua原生支援 否 輸出
虛數單位
i
否 是 是 是 否 輸出
j單位
j
否 否 是 否 否 輸出
k單位
k
否 否 是 否 否 輸出
絕對值
abs(a)
是 是 是 lua原生支援 回傳1或0 輸出
符号函数
sgn(a)
否 是 是 是 回傳1或0 輸出
共轭复数
conjugate(a)
否 是 是 原式輸出。 否 輸出
輻角
arg(a)
否 是 是 是 否 輸出
平方根
sqrt(a)
否 是 是 是 否 輸出
倒數
inverse(a)
否 是 是 是 否 輸出
分數
div(a,b)
否 是 是 是 否 輸出
數字部件 實部
re(a)
否 是 是 是 否 輸出
虛部
im(a)
否 是 是 恆為0 否 輸出
非實部
nonRealPart(a)
否 是 是 恆為0 恆為0 即将到来
純量部 否 否 是 否 否 否
向量部 否 否 是 否 否 否
部件向量
tovector(a)
否 是 是 單一元素向量 否 否
內積
dot(a,b)
否 是 是 與乘法相同 否 輸出
外積
outer(a,b)
不存在 恆為0 是 不存在 不存在 即将到来

a ^ b
是 只能pow(a,b) 只能pow(a,b) lua原生支援 否 只能pow(a,b)
指對數函數 指數
pow(a,b)
是 是 是 lua原生支援 否 輸出
自然對數
log(a)
是 是 是 lua原生支援 不存在 輸出
自然指數日语指数関数
exp(a)
是 是 是 lua原生支援 不存在 輸出
cis
cis(a)
否 是 是 是 不存在 輸出
高斯符號 地板
floor(a)
是 是 是 lua原生支援 不存在 輸出
天花板
ceil(a)
是 是 是 lua原生支援 不存在 輸出
数值修约
round(a)
否 是 是 是 不存在 即将到来
截尾函數
trunc(a,b)
否 是 是 是 不存在 輸出
三角函數 正弦
sin(a)
是 是 是 lua原生支援 不存在 輸出
餘弦
cos(a)
是 是 是 lua原生支援 不存在 輸出
正切
tan(a)
是 是 是 lua原生支援 不存在 輸出
餘切
cot(a)
否 是 是 是 不存在 輸出
反三角函數 反正弦
asin(a)
是 是 是 lua原生支援 不存在 輸出
反餘弦
acos(a)
是 是 是 lua原生支援 不存在 輸出
反正切
atan(a)
是 是 是 lua原生支援 不存在 輸出
反餘切
acot(a)
否 是 是 是 不存在 輸出
雙曲函數 雙曲正弦
sinh(a)
是 是 是 lua原生支援 不存在 輸出
雙曲餘弦
cosh(a)
是 是 是 lua原生支援 不存在 輸出
雙曲正切
tanh(a)
是 是 是 lua原生支援 不存在 輸出
雙曲餘切
coth(a)
否 是 是 是 不存在 輸出
反雙曲函數 雙曲反正弦
asinh(a)
否 是 是 是 不存在 輸出
雙曲反餘弦
acosh(a)
否 是 是 是 不存在 輸出
雙曲反正切
atanh(a)
否 是 是 是 不存在 輸出
雙曲反餘切
acoth(a)
否 是 是 是 不存在 輸出

擴充函數

本模組僅為這些數學結構定義一些基本運算(見上表)。一些較複雜的運算可透過調用Module:Complex_Number/Functions來完成。本模組提供的3個部分(cmath、qmath、math)皆支援Module:Complex_Number/Functions

使用方法
mathlib = require("Module:Complex Number/Functions")._init(mathlib, numberConstructer)
其中,mathlib為已初始化的數學函數庫(如cmath、qmath、math),numberConstructer為對應該數學函數庫數字結構的建構子函數。
所回傳的新mathlib將會包含Module:Complex_Number/Functions中已定義的所有擴充函數。
註:詳細使用條件參見Module:Complex_Number/Functions/doc#使用條件,說明了函數庫須具備那些條件方能使用此擴充功能。

定義新的數學庫

Module:Complex Number是一系列數學運算庫,並可以相互兼容。當然也能定義其他兼容的程式庫,但需要符合特定條件,例如需要實作一些需求函數。詳細內容可以參考範例數學庫Module:Complex Number/Example

若要定義一個新的Module:Complex Number系列函數庫需要實作一個新的物件,並實作其Metatables中的運算子。

定義數學資料結構

參閱Example的第85行

數學資料結構需要定義成一個table,並以table來定義或表達所需要的數字。即使數字只有單一物件,也許使用table因為這樣才能透過實作Metatables來完成Module:Complex Number系列函數庫所需的相關功能。

實作metatable

參閱Example的第47行

需定義Metatables的 __add(加法)、 __sub(減法)、 __mul(乘法)、 __div(除法)、 __mod(取餘數)、 __unm(相反數)、 __eq(相等判斷)、 __tostring(以字串表達本物件)

定義數學資料結構的建構子

參閱Example的第85行第95行

由於數學資料結構需要定義為table因此需要有建構子來賦予該結構初值。建構子需要完成以下步驟:

定義數學庫的初始化函數

參閱Example的第101行

數學庫必須是一個獨立物件,所有的函數皆需定義在數學函數庫物件下(包括數學資料結構的建構子)。初始化數學庫的函數名稱必為init,當中需要定義以下內容:

完成數學庫的定義

視情況定義列於Module:Complex_Number/doc#比較中的各項函數(如需支援Module:Complex_Number/Functions的情況)。

其他函數庫

require("Module:Complex Number").cmath
複變函數庫
require("Module:Complex Number").qmath
四元數函數庫
require("Module:Complex Number").math
實數函數庫擴充
require("Module:Complex Number").bmath
布林代數函數庫
require("Module:Complex Number/Calculate").tagmath
輸出為<math></math>的運算庫
require("Module:Complex Number/Matrix").mmath
矩陣函數庫
require("Module:Complex Number/Dual Number").dumath
二元數函數庫
require("Module:Complex Number/Dual Number").ducmath
二元複數英语Applications of dual quaternions to 2D geometry函數庫
require("Module:Complex Number/Octonion").omath
八元數函數庫
require("Module:Complex Number/CayleyDickson").cdmath.init(math_lib)
將指定的函數庫math_lib套用凯莱-迪克森结构形成新的函數庫(無法自我嵌套)
require("Module:Complex Number/CayleyDickson").sdmath
八元數套用凯莱-迪克森结构後的形成新的十六元數函數庫
require("Module:Complex Number/CayleyDickson").cdmathOctonion
預先套用凯莱-迪克森结构八元數後的函數庫(可作為十六元數使用)
require("Module:Complex Number/CayleyDickson").cdmathSedenion
預先套用凯莱-迪克森结构十六元數後的函數庫(可作為三十二元數使用)

相關頁面

--'
local p = { PrimeTable = {} }
local numlib = require("Module:Number")
local numdata = require("Module:Number/data")
local calclib = require("Module:Complex Number/Calculate")
local sollib = require("Module:Complex_Number/Solver")

p._numberType = sollib._numberType
p._isNaN = sollib._isNaN

--debug
--local cmath,tonum=p.cmath.init(),p.cmath.init().toComplexNumber; mw.logObject(cmath.abs(cmath.nonRealPart(tonum("2+3i"))))
local eReal, eImag = 'reω', 'ω'
p.cmath = {
	abs=function(z)
		local real, imag = p.cmath.readPart(z)
		if math.abs(imag) < 1e-12 then return math.abs(real) end
		return math.sqrt(real * real + imag * imag)
	end,
	floor=function(z)
		local real, imag = p.cmath.readPart(z)
		return p.cmath.getComplexNumber(math.floor(real), math.floor(imag)) 
	end,
	ceil=function(z)
		local real, imag = p.cmath.readPart(z)
		return p.cmath.getComplexNumber(math.ceil(real), math.ceil(imag)) 
	end,
	round=function(op1,op2,op3)
		local number = p.cmath.getComplexNumber(tonumber(op1) or op1.real or 0, (tonumber(op1) and 0) or op1.imag or 0) 
		local digs = p.cmath.getComplexNumber(tonumber(op2) or (op2 or {}).real or 0, (tonumber(op2) and 0) or (op2 or {}).imag or 0) 
		local base = p.cmath.getComplexNumber(tonumber(op3) or (op3 or {}).real or 10, (tonumber(op3) and 0) or (op3 or {}).imag or 0) 
		local round_rad = p.cmath.pow(base,digs)
		local check_number = number * round_rad
		check_number.real = check_number.real + 0.5; check_number.imag = check_number.imag + 0.5; 
		return p.cmath.floor( check_number ) / round_rad
	end,
	div=function(op1,op2)
		local a, c = tonumber(op1) or op1.real, tonumber(op2) or op2.real
		local b, d = (tonumber(op1) and 0) or op1.imag, (tonumber(op2) and 0) or op2.imag
		local op1_d, op2_d = a*a + b*b, c*c + d*d
		if op2_d <= 0 then return op1_d / op2_d end
		return p.cmath.getComplexNumber((a * c + b * d) / op2_d, (b * c - a * d) / op2_d) 
	end,
	re=function(z)return tonumber(z) or z.real end,
	im=function(z) return (tonumber(z) and 0) or z.imag end,
	nonRealPart=function(z) return p.cmath.getComplexNumber(0, (tonumber(z) and 0) or z.imag) end,
	conjugate=function(z)
		local real, imag = p.cmath.readPart(z)
		return p.cmath.getComplexNumber(real, -imag)
	end,
	inverse=function(z)
		local real, imag = p.cmath.readPart(z)
		return p.cmath.getComplexNumber(real, -imag) / ( real*real + imag*imag )
	end,
	tovector=function(z)
		return {p.cmath.readPart(z)}
	end,
	trunc=function(z,digs)
		local real, imag = p.cmath.readPart(z)
		local n = tonumber(digs) or digs.real or 0
		return p.cmath.getComplexNumber(sollib._trunc(real,n), sollib._trunc(imag,n))
	end,
	digits=function(z)
		local real, imag = p.cmath.readPart(z)
		real, imag = math.floor(math.abs(real)), math.floor(math.abs(imag))
		return math.max(tostring(real):len(),tostring(imag):len())
	end,
	--判斷是否為第一象限高斯質數
	is_prime_quadrant1=function(z)
		local real, imag = p.cmath.readPart(z)
		if imag == 0 and real == 0 then return false end
		if not numdata._is_integer(imag) or not numdata._is_integer(real) then return false end
		if imag == 0 then 
			if real <= 1 then return false end
			if numdata._is_integer((real - 3.0) / 4.0) then
				if p.PrimeTable.table_max == nil then p.PrimeTable = require('Module:Factorization') end
				p.PrimeTable.primeIndexOf({(real or 0)+2})
				return p.PrimeTable.lists[real] ~= nil
			end
		end
		--非第一象限高斯質數
		if imag < 0 or real < 0 then return false end
		if imag ~= 0 and real == 0 then return false end
		local value = imag*imag + real*real
		--both are nonzero and a² + b² is a prime number (which will not be of the form 4n + 3).
		if numdata._is_integer((value - 3.0) / 4.0) then return false end
		if p.PrimeTable.table_max == nil then p.PrimeTable = require('Module:Factorization') end
		p.PrimeTable.primeIndexOf({value+2})
		return p.PrimeTable.lists[value] ~= nil
	end,
	sqrt=function(z)
		local real, imag = p.cmath.readPart(z)
		local argument = 0
		local length = math.sqrt( real * real + imag * imag )
		if imag ~= 0 then
			argument = 2.0 * math.atan(imag / (length + real))
		else
			if real > 0 then argument = 0.0 
			else argument = math.pi end
		end
		local sq_len = math.sqrt(length)
		return p.cmath.getComplexNumber(sq_len * math.cos(argument/2.0), sq_len * math.sin(argument/2.0)):clean()
	end,
	root=function(_z,_n,_num)
		local z = p.cmath.getComplexNumber(p.cmath.readPart(_z))
		local n = p.cmath.getComplexNumber(p.cmath.readPart(_n or 2))
		local num = p.cmath.getComplexNumber(p.cmath.readPart(_num or 1))
		if num == p.cmath.one or num == p.cmath.zero or num == nil then
			return p.cmath.pow(z, p.cmath.inverse(n))
		end
		local sgn_data = p.cmath.getComplexNumber(0, 1)
		local result = p.cmath.pow(p.cmath.abs(z), p.cmath.inverse(n)) * p.cmath.exp(sgn_data * (p.cmath.arg(z) + (num-1)*(2*math.pi) ) * p.cmath.inverse(n))
		result:clean()
		return result
	end,
	sin=function(z)
		local real, imag = p.cmath.readPart(z)
		return p.cmath.getComplexNumber(math.sin(real) * math.cosh(imag), math.cos(real) * math.sinh(imag)) 
	end,
	cos=function(z)
		local real, imag = p.cmath.readPart(z)
		return p.cmath.getComplexNumber(math.cos(real) * math.cosh(imag), -math.sin(real) * math.sinh(imag)) 
	end,
	tan=function(z)
		local theta = p.cmath.readComplexNumber(z)  
		return p.cmath.sin(theta) / p.cmath.cos(theta)
	end,
	cot=function(z)
		local theta = p.cmath.readComplexNumber(z)  
		return p.cmath.cos(theta) / p.cmath.sin(theta)
	end,

	asin=function(z)
		local real, imag = p.cmath.readPart(z)
		local u, v = p.cmath.getComplexNumber(0, imag), p.cmath.getComplexNumber(real, imag)
		local sgnimag = p.cmath.sgn(u); if math.abs(sgnimag.imag) < 1e-12 then sgnimag.imag = 1 end
		return -sgnimag * p.cmath.asinh( v * sgnimag )
	end,
	acos=function(z)
		local real, imag = p.cmath.readPart(z)
		local u, v = p.cmath.getComplexNumber(0, imag), p.cmath.getComplexNumber(real, imag)
		local sgnimag = p.cmath.sgn(u); if math.abs(sgnimag.imag) < 1e-12 then sgnimag.imag = 1 end
		return -sgnimag * p.cmath.acosh( v )
	end,
	atan=function(z)
		local real, imag = p.cmath.readPart(z)
		local u, v = p.cmath.getComplexNumber(0, imag), p.cmath.getComplexNumber(real, imag)
		local sgnimag = p.cmath.sgn(u); if math.abs(sgnimag.imag) < 1e-12 then sgnimag.imag = 1 end
		return -sgnimag * p.cmath.atanh( v * sgnimag )
	end,
	acot=function(z)
		local real, imag = p.cmath.readPart(z)
		local u, v = p.cmath.getComplexNumber(0, imag), p.cmath.getComplexNumber(real, imag)
		local sgnimag = p.cmath.sgn(u); if math.abs(sgnimag.imag) < 1e-12 then sgnimag.imag = 1 end
		return sgnimag * p.cmath.acoth( v * sgnimag )
	end,
	
	sinh=function(z)
		local real, imag = p.cmath.readPart(z)
		local im_sgn if imag > 0 then im_sgn = 1 elseif imag < 0 then im_sgn = -1 else im_sgn = 0 end
		return p.cmath.getComplexNumber( math.cos(math.abs(imag)) * math.sinh(real) , im_sgn * math.sin(math.abs(imag)) * math.cosh(real) )
	end,
	cosh=function(z)
		local real, imag = p.cmath.readPart(z)
		local im_sgn if imag > 0 then im_sgn = 1 elseif imag < 0 then im_sgn = -1 else im_sgn = 0 end
		return p.cmath.getComplexNumber( math.cos(math.abs(imag)) * math.cosh(real) , im_sgn * math.sin(math.abs(imag)) * math.sinh(real) )
	end,
	tanh=function(z)
		local theta = p.cmath.readComplexNumber(z)
		return p.cmath.sinh(theta) / p.cmath.cosh(theta)
	end,
	coth=function(z)
		local theta = p.cmath.readComplexNumber(z)
		return p.cmath.cosh(theta) / p.cmath.sinh(theta)
	end,

	asinh=function(z)
		local real, imag = p.cmath.readPart(z)
		local u = p.cmath.getComplexNumber(real, imag)
		return p.cmath.log( u + p.cmath.sqrt( u * u + p.cmath.getComplexNumber(1,0) ) )
	end,
	acosh=function(z)
		local real, imag = p.cmath.readPart(z)
		local u = p.cmath.getComplexNumber(real, imag)
		return p.cmath.log( u + p.cmath.sqrt( u + p.cmath.getComplexNumber(1,0) ) * p.cmath.sqrt( u + p.cmath.getComplexNumber(-1,0) ) )
	end,
	atanh=function(z)
		local real, imag = p.cmath.readPart(z)
		local u = p.cmath.getComplexNumber(real, imag)
		return ( p.cmath.log( 1 + u ) - p.cmath.log( 1 - u ) ) / 2
	end,
	acoth=function(z)
		local real, imag = p.cmath.readPart(z)
		local u = p.cmath.getComplexNumber(real, imag)
		return ( p.cmath.log( 1 + p.cmath.inverse(u) ) - p.cmath.log( 1 - p.cmath.inverse(u) ) ) / 2
	end,

	dot=function (op1, op2)
		local real1, imag1 = p.cmath.readPart(op1)
		local real2, imag2 = p.cmath.readPart(op2)
		return real1 * real2 + imag1 * imag2 
	end,
	outer = function (op1, op2) 
		return p.cmath.getComplexNumber(0, 0)
	end,
	sgn=function(z)
		local real, imag = p.cmath.readPart(z)
		if real == 0 and imag == 0 then return p.cmath.getComplexNumber(0, 0) end
		local length = math.sqrt( real * real + imag * imag )
		return p.cmath.getComplexNumber(real/length, imag/length)
	end,
	arg=function(z)
		local real, imag = p.cmath.readPart(z)
		if imag ~= 0 then
			local length = math.sqrt( real * real + imag * imag )
			return 2.0 * math.atan(imag / (length + real))
		else
			if real >= 0 then return 0.0 
			else return math.pi end
		end
		return tonumber("nan")
	end,
	cis=function(z)
		local real, imag = p.cmath.readPart(z)
		local hyp = 1
		if imag ~= 0 then hyp = math.cosh(imag) - math.sinh(imag) end
		return p.cmath.getComplexNumber(math.cos(real) * hyp, math.sin(real) * hyp)
	end,
	exp=function(z)
		local real, imag = p.cmath.readPart(z)
		local cis_r, cis_i, exp_r = 1, 0, math.exp(real)
		if imag ~= 0 then cis_r, cis_i = math.cos(imag), math.sin(imag) end
		return p.cmath.getComplexNumber(exp_r * cis_r, exp_r * cis_i)
	end,
	elog=function(z)
		local real, imag = p.cmath.readPart(z)
		local argument = 0
		local length = math.sqrt( real * real + imag * imag )
		if imag ~= 0 then
			argument = 2.0 * math.atan(imag / (length + real))
		else
			if real > 0 then argument = 0.0 
			else argument = math.pi end
		end
		return p.cmath.getComplexNumber(math.log(length), argument)
	end,
	log=function(z,basez)
		if basez~=nil then return p.cmath.elog(basez) * p.cmath.inverse(p.cmath.elog(z)) end
		return p.cmath.elog(z)
	end,
	eisenstein=function(op1)
		local real1, imag1 = tonumber(op1) or op1.real,  (tonumber(op1) and 0) or op1.imag
		local sqrt32, sqrt33 = math.sqrt(3)/2, 1/math.sqrt(3)
		return p._eisenstein_integer(real1+sqrt33*imag1, 2*sqrt33*imag1)
	end,
	pow=function(op1,op2)
		local check_op1, check_op2 = tonumber(tostring(op1)) or -1, tonumber(tostring(op2)) or -1
		if check_op1 == 1 then return p.cmath.getComplexNumber(1,0) end -- 1^z === 1
		if check_op2 == 1 then return op1 end -- z^1 === z
		if check_op2 == 0 then -- z^0
			if check_op1 ~= 0 then return p.cmath.getComplexNumber(1,0) -- z^0 === 1, z ≠ 0
			else return p.cmath.getComplexNumber(tonumber('nan'),0) end -- 0^0 Indeterminate
		elseif check_op1 == 0 then 
			if check_op2 < 0 then return p.cmath.getComplexNumber(tonumber('inf'),0) end -- 0^(-n) Infinity
			return p.cmath.getComplexNumber(0,0) -- 0^z === 0, z ≠ 0
		end
		--a ^ z
		local a = p.cmath.getComplexNumber( tonumber(op1) or op1.real, (tonumber(op1) and 0) or op1.imag )
		local z = p.cmath.getComplexNumber( tonumber(op2) or op2.real, (tonumber(op2) and 0) or op2.imag )
		return p.cmath.exp(z * p.cmath.log(a)):clean()
	end,

	random = function (op1, op2)
		if type(op1)==type(nil) and type(op2)==type(nil) then return p.cmath.getComplexNumber(math.random(),0) end
		local real1, real2 = tonumber(op1) or op1.real, tonumber(op2) or (op2 or{}).real
		local imag1, imag2 = (tonumber(op1) and 0) or op1.imag, (tonumber(op2) and 0) or (op2 or{}).imag
		if type(op2)==type(nil) then return p.cmath.getComplexNumber(sollib._random(real1), sollib._random(imag1)) end
		return p.cmath.getComplexNumber(sollib._random(math.min(real1,real2), math.max(real1,real2)), sollib._random(math.min(imag1,imag2), math.max(imag1,imag2))) 
	end,

	isReal=function(z) return math.abs((tonumber(z) and 0) or z.imag) < 1e-14 end,
	
	ComplexNumberMeta = {
		__add = function (op1, op2) 
			local real1, real2 = tonumber(op1) or (op1 or {}).real, tonumber(op2) or (op2 or {}).real
			local imag1, imag2 = (tonumber(op1) and 0) or (op1 or {}).imag, (tonumber(op2) and 0) or (op2 or {}).imag
			return p.cmath.getComplexNumber(real1 + real2, imag1 + imag2) 
		end,
		__sub = function (op1, op2) 
			local real1, real2 = tonumber(op1) or (op1 or {}).real, tonumber(op2) or (op2 or {}).real
			local imag1, imag2 = (tonumber(op1) and 0) or (op1 or {}).imag, (tonumber(op2) and 0) or (op2 or {}).imag
			return p.cmath.getComplexNumber(real1 - real2, imag1 - imag2) 
		end,
		__mul = function (op1, op2) 
			local a, c = tonumber(op1) or (op1 or {}).real, tonumber(op2) or (op2 or {}).real
			local b, d = (tonumber(op1) and 0) or (op1 or {}).imag, (tonumber(op2) and 0) or (op2 or {}).imag
			return p.cmath.getComplexNumber(a * c - b * d, b * c + a * d) 
		end,
		__div = function (op1, op2) 
			local a, c = tonumber(op1) or (op1 or {}).real, tonumber(op2) or (op2 or {}).real
			local b, d = (tonumber(op1) and 0) or (op1 or {}).imag, (tonumber(op2) and 0) or (op2 or {}).imag
			local op1_d, op2_d = a*a + b*b, c*c + d*d
			if op2_d <= 0 then return op1_d / op2_d end
			return p.cmath.getComplexNumber((a * c + b * d) / op2_d, (b * c - a * d) / op2_d) 
		end,
		__mod = function (op1, op2) 
			local x = p.cmath.getComplexNumber(tonumber(op1) or (op1 or {}).real, (tonumber(op1) and 0) or (op1 or {}).imag)
			local y = p.cmath.getComplexNumber(tonumber(op2) or (op2 or {}).real, (tonumber(op2) and 0) or (op2 or {}).imag) 
			return x - y * p.cmath.floor(x / y) 
		end,
		__tostring = function (this) 
			local body = ''
			if this.real ~= 0 then body = tostring(this.real) end
			if this.imag ~= 0 then 
				if body ~= '' and this.imag > 0 then body = body .. '+' end
				if this.imag == -1 then  body = body .. '-' end
				if math.abs(this.imag) ~= 1 then body = body .. tostring(this.imag) end
				body = body .. 'i'
			end
			if sollib._isNaN(this.real) or sollib._isNaN(this.imag) then body = 'nan' end
			if body == '' then body = '0' end
			return body
		end,
		__unm = function (this)
			return p.cmath.getComplexNumber(-this.real, -this.imag) 
		end,
		__eq = function (op1, op2)
			local diff_real = math.abs( (tonumber(op1) or (op1 or {}).real) - (tonumber(op2) or (op2 or {}).real) )
			local diff_imag1 = math.abs( ( (tonumber(op1) and 0) or (op1 or {}).imag) - ( (tonumber(op2) and 0) or (op2 or {}).imag) )
			return diff_real < 1e-12 and diff_imag1 < 1e-12
		end,
	},
	readComplexNumber = function(z)
		if type(z) == type({}) then --if already be complex number, don't run string find.
			if z.numberType == "complex" then
				return z
			elseif z.numberType == "quaternion" then
				return p.cmath.getComplexNumber(z.real, z.imag)
			end
		elseif type(z) == type(0) then
			return p.cmath.getComplexNumber(z, 0)
		elseif type(z) == type(true) then
			return p.cmath.getComplexNumber(z and 1 or 0, 0)
		end
		return p.cmath.getComplexNumber(tonumber(z) or (z or {}).real or tonumber(tostring(z)) or 0, ((tonumber(z) or tonumber(tostring(z))) and 0) or (z or {}).imag or 0)
	end,
	readPart = function(z)
		if type(z) == type({}) and (z.numberType == "complex" or z.numberType == "quaternion") then --if already be complex number, don't run string find.
			return z.real, z.imag
		elseif type(z) == type(0) then
			return z, 0
		elseif type(z) == type(true) then
			return z and 1 or 0, 0
		end
		return tonumber(z) or (z or {}).real or tonumber(tostring(z)) or 0, ((tonumber(z) or tonumber(tostring(z))) and 0) or (z or {}).imag or 0
	end,
	ele=function(id)
		local _zero = p.cmath.getComplexNumber(0, 0)
		local eles = (p.cmath.elements or {})
		local id_msg = tonumber(tostring(id)) or 0
		return eles[id_msg+1]or _zero
	end,
	getComplexNumber = function(real,imag)
		local ComplexNumber = {}
		setmetatable(ComplexNumber,p.cmath.ComplexNumberMeta) 
		function ComplexNumber:update()
			self.argument = 0
			self.length = math.sqrt( self.real * self.real + self.imag * self.imag )
			if self.imag ~= 0 then
				self.argument = 2.0 * math.atan(self.imag / (self.length + self.real))
			else
				if self.real > 0 then self.argument = 0.0 
				else self.argument = math.pi end
			end
		end
		function ComplexNumber:clean()
			if math.abs(self.real) <= 1e-12 then self.real = 0 end
			if math.abs(self.imag) <= 1e-12 then self.imag = 0 end
			if math.abs(self.real - math.floor(self.real)) <= 1e-12 then self.real = math.floor(self.real) end
			if math.abs(self.imag - math.floor(self.imag)) <= 1e-12 then self.imag = math.floor(self.imag) end
			return self
		end
		ComplexNumber.real, ComplexNumber.imag = real, imag
		ComplexNumber.numberType = "complex"
		return ComplexNumber
	end,
	toComplexNumber = function(num_str)
		if type(num_str)==type({"table"}) and num_str.isEisensteinNumber == true then 
			real, imag = tonumber(num_str) or num_str.real, (tonumber(num_str) and 0) or num_str.imag
			local sqrt32, sqrt33 = math.sqrt(3)/2, 1/math.sqrt(3)
			local eis = p._eisenstein_integer(real+sqrt33*imag, 2*sqrt33*imag)
			eis.real,eis.imag = real, imag
			return eis 
		end
		if type(num_str) == type({}) then --if already be complex number, don't run string find.
			if num_str.numberType == "complex" then
				return num_str
			elseif num_str.numberType == "quaternion" then
				return p.cmath.getComplexNumber(num_str.real, num_str.imag)
			end
		elseif type(num_str) == type(0) then
			return p.cmath.getComplexNumber(num_str, 0)
		elseif type(num_str) == type(true) then
			return p.cmath.getComplexNumber(num_str and 1 or 0, 0)
		elseif type(num_str) == type("string") then
			local check_number = tonumber(num_str)
			if check_number ~= nil then return p.cmath.getComplexNumber(check_number, 0) end
		end
		local real, imag
		if num_str == nil then return nil end
		if ( type(num_str)==type(0) or ( (type(num_str)==type({"table"})) and type(num_str.real)==type(0) ) ) then
			real, imag = tonumber(num_str) or num_str.real, (tonumber(num_str) and 0) or num_str.imag
		else real, imag = p.cmath.toComplexNumberPart(num_str)end
		if real == nil or imag == nil then return nil end
		return p.cmath.getComplexNumber(real, imag)
	end,
	toComplexNumberPart = function(num_str)
		if type(num_str) == type(function()end) then return end
		if type(num_str) == type(true) then if num_str then return 1,0 else return 0,0 end end
		local body = ''
		local real, imag = 0, 0
		local split_str = mw.text.split(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(
				tostring(num_str) or '',
			'%s+',''),'%++([%d%.])',',+%1'),'%++([ij])',',+1%1'),'%-+([%d%.])',',-%1'),'%-+([ij])',',-1%1'),'%*+([%d%.])',',*%1'),'%*+([ij])',',*1%1'),'%/+([%d%.])',',/%1'),'%/+([ij])',',/1%1'),',')
		local first = true
		local continue = false
		
		for k,v in pairs(split_str) do
			continue = false
			local val = mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.text.trim(v),'[ij]+','i'),'^(%.)','0%1'),'^([%d%.])','+%1'),'([%+%-])([%d%.])','%1\48%2'),'^([ij])','+1%1')
			if mw.ustring.find(val,"%/") or mw.ustring.find(val,"%*") then return end
			if val == nil or val == '' then if first == true then first = false continue = true else return end end
			if not continue then
				local num_text = mw.ustring.match(val,"[%+%-][%d%.]+i?")
				if num_text ~= val then return end
				local num_part = tonumber(mw.ustring.match(num_text,"[%+%-][%d%.]+"))
				if num_part == nil then return end
				if mw.ustring.find(num_text,"i") then
					imag = imag + num_part
				else
					real = real + num_part
				end
			end
		end
		return real, imag
	end,
	halfNumberParts = function(num)
		local real, imag = p.cmath.readPart(num)
		return {real, imag}
	end,
	init = function()
		p.cmath.e = p.cmath.getComplexNumber(math.exp(1), 0) 
		p.cmath.pi = p.cmath.getComplexNumber(math.pi, 0) 
		p.cmath["π"] = p.cmath.getComplexNumber(math.pi, 0)
		p.cmath["°"] = p.cmath.getComplexNumber(math.pi/180, 0)
		p.cmath.nan = p.cmath.getComplexNumber(tonumber("nan"), tonumber("nan")) 
		p.cmath.infi = p.cmath.getComplexNumber(0, tonumber("inf")) 
		p.cmath.zero = p.cmath.getComplexNumber(0, 0) 
		p.cmath.one = p.cmath.getComplexNumber(1, 0) 
		p.cmath[-1] = p.cmath.getComplexNumber(-1, 0) 
		p.cmath[eImag] = p._eisenstein_integer(0, 1)
		p.cmath.i = p.cmath.getComplexNumber(0, 1) 
		p.cmath[0],p.cmath[1] = p.cmath.zero,p.cmath.one
		p.cmath.numberType = sollib._numberType
		p.cmath.constructor = p.cmath.toComplexNumber
		p.cmath.elements = {
			p.cmath.getComplexNumber(1, 0),
			p.cmath.getComplexNumber(0, 1)
		}
		return p.cmath
	end
}
p.math={
	init = function()
		local my_math = math 
		my_math.e, my_math.nan = math.exp(1), tonumber("nan")
		my_math["π"] = math.pi
		my_math["°"] = math.pi/180
		my_math.zero, my_math.one, my_math[-1] = 0, 1, -1
		my_math[0],my_math[1] = my_math.zero,my_math.one
		
		my_math.inverse = function(x)return 1.0/(tonumber(x)or 1.0)end
		my_math.sgn=function(x)if x==0 then return 0 elseif x<0 then return -1 elseif x>0 then return 1 else return tonumber("nan")end end
		my_math.arg=function(x)if x >= 0 then return 0.0 else return math.pi end end
		my_math.re=function(z) return tonumber(z) or z.real or 0 end
		my_math.im=function(z) return (tonumber(z) and 0) or z.imag or 0 end
		my_math.conjugate=function(z) return tonumber(z) or z.real or 0 end
		my_math.root=function(z,n) return math.pow((tonumber(z)or 1.0), (1.0/(tonumber(n)or 1.0))) end
		my_math.nonRealPart=function(z) return (tonumber(z) and 0) or z.imag or 0 end
		my_math.tovector=function(z) return {tonumber(z) or z.real or 0} end
		my_math.trunc=function(z,digs) 
			local x = tonumber(z) or z.real or 0
			local n = tonumber(digs) or digs.real or 0
			local _10_n = math.pow(10,n)
			local _10_n_x = _10_n * x
			return (x >= 0)and(math.floor(_10_n_x) / _10_n)or(math.ceil(_10_n_x) / _10_n)
		end
		my_math.div=function(op1,op2) return tonumber(op1) / tonumber(op2) end
		my_math.dot=function(x,y)return x*y end
		
		--sin, cos, tan are already support
		my_math.cot=function(z)local theta = tonumber(z)return math.cos(theta) / math.sin(theta)end
		
		--asin, acos, atan are already support
		my_math.acot=function(x)return p.cmath.acot(x).real end
		
		--sinh, cosh, tanh are already support
		my_math.coth=function(x)return math.cosh(x) / math.sinh(x) end
		
		my_math.asinh=function(x)return math.log( x + math.sqrt( x * x + 1 ) )end
		my_math.acosh=function(x)return math.log( x + math.sqrt( x * x - 1 ) )end
		my_math.atanh=function(x) local result = p.cmath.atanh(x):clean() if math.abs(result.imag) > 1e-12 then return tonumber('nan') else return result.real end end
		my_math.acoth=function(x) local result = p.cmath.acoth(x):clean() if math.abs(result.imag) > 1e-12 then return tonumber('nan') else return result.real end end
		
		my_math.ele=function(id)
			if (tonumber(tostring(id))or -1) == 0 then return 1 end
			return 0
		end

		my_math.isReal = function(x) if type(x)==type(true) then return true else return tonumber(x)~=nil end end

		my_math.numberType = sollib._numberType
		my_math.constructor = function(x) if type(x)==type(true) then return x and 1 or 0 else return tonumber(x) end end

		my_math.elements = {1}
		return my_math
	end
}
p.bmath={
	abs=function(_z)
		local z = p.bmath.toBoolean(_z)
		return (not not z.value) and 1 or 0
	end,
	sgn=function(_z)
		local z = p.bmath.toBoolean(_z)
		return (not not z.value) and 1 or 0
	end,
	as=function(op1, op2)
		local a, b = p.bmath.toBoolean(op1) or p.bmath.toBoolean(false), p.bmath.toBoolean(op2) or p.bmath.toBoolean(false)
		b.value = a.value
		return b 
	end,
	nonRealPart=function(bv) return p.bmath.toBoolean(0) end,
	BooleanNumberMeta = {
		__add = function (op1, op2) 
			local a, b = p.bmath.toBoolean(op1) or p.bmath.toBoolean(false), p.bmath.toBoolean(op2) or p.bmath.toBoolean(false)
			a.value = a.value or b.value
			return a 
		end,
		__sub = function (op1, op2) 
			local a, b = p.bmath.toBoolean(op1) or p.bmath.toBoolean(false), p.bmath.toBoolean(op2) or p.bmath.toBoolean(false)
			a.value = a.value and (not b.value)
			return a 
		end,
		__mul = function (op1, op2) 
			local a, b = p.bmath.toBoolean(op1) or p.bmath.toBoolean(false), p.bmath.toBoolean(op2) or p.bmath.toBoolean(false)
			a.value = a.value and b.value
			return a 
		end,
		__tostring = function (this) return this.value_table[this.value] end,
		__unm = function (this)local that = p.bmath.toBoolean(this)that.value = not that.value return that end,
		__eq = function (op1, op2)return p.bmath.toBoolean(op1).value == p.bmath.toBoolean(op2).value end,
	},
	value_table={
		[1]={[true]=1,[false]=0},[0]={[true]=1,[false]=0},
		['1']={[true]=1,[false]=0},['0']={[true]=1,[false]=0},
		['yes']={[true]='yes',[false]='no'},['no']={[true]='yes',[false]='no'},
		['y']={[true]='Y',[false]='N'},['n']={[true]='Y',[false]='N'},
		[true]={[true]=true,[false]=false},[false]={[true]=true,[false]=false},
		['true']={['true']=true,['false']=false},['false']={['true']=true,['false']=false},
		['t']={[true]='T',[false]='F'},['f']={[true]='T',[false]='F'},
		['on']={[true]='on',[false]='off'},['off']={[true]='on',[false]='off'},
		['是']={[true]='是',[false]='否'},['否']={[true]='是',[false]='否'},
		['真']={[true]='真',[false]='假'},['假']={[true]='真',[false]='假'},
		['有']={[true]='有',[false]='無'},['無']={[true]='有',[false]='無'},['无']={[true]='有',[false]='无'},
		['开']={[true]='开',[false]='关'},['关']={[true]='开',[false]='关'},
		['開']={[true]='開',[false]='關'},['關']={[true]='開',[false]='關'},},
	toBoolean = function(num_str)
		local BooleanNumber = {}
		if (type(num_str) == type({}) and num_str.numberType == "boolean") then return num_str end
		if (type(num_str) == type({})) and num_str.value_table ~= nil and num_str.value ~= nil then 
			BooleanNumber = {value_table=num_str.value_table,value=num_str.value}
		elseif (type(num_str) == type({}) and type(num_str.numberType)==type("string") and num_str.numberType ~= "boolean")then 
			local data = tostring(num_str) ~= "0"
			BooleanNumber = {}
			BooleanNumber.value_table={[true]='T',[false]='F'}
			BooleanNumber.value=data
		elseif (type(num_str) == type(0))then 
			local data = math.abs(num_str) > 1e-14
			BooleanNumber = {}
			BooleanNumber.value_table={[true]='T',[false]='F'}
			BooleanNumber.value=data
		elseif (type(num_str) == type(true))then 
			local data = num_str
			BooleanNumber = {}
			BooleanNumber.value_table={[true]='T',[false]='F'}
			BooleanNumber.value=data
		else
			if yesno == nil then yesno = require('Module:Yesno') end
			local input_str = mw.ustring.gsub(mw.text.trim(tostring(num_str)),"%s",'')
			input_str = mw.ustring.gsub(input_str,"([真假有無无])",function(str) return(
				{['真']='是',['假']='否',['有']='是',['無']='否',['无']='否'})[str] or str end)
			local data = yesno(num_str) or yesno(input_str)
			if data == nil then return nil end
			BooleanNumber = {}
			BooleanNumber.value_table=p.bmath.value_table[num_str] or p.bmath.value_table[input_str] or {[true]='T',[false]='F'}
			BooleanNumber.value=data
		end
		setmetatable(BooleanNumber,p.bmath.BooleanNumberMeta) 
		BooleanNumber.numberType = "boolean"
		return BooleanNumber
	end,
	init = function()
		p.bmath.zero = p.bmath.toBoolean(0) 
		p.bmath.one = p.bmath.toBoolean(1) 
		p.bmath[0],p.bmath[1] = p.bmath.zero,p.bmath.one
		p.bmath.is_bool_lib = true
		p.bmath.numberType = sollib._numberType
		p.bmath.constructor = p.bmath.toBoolean
		p.bmath.elements = {true}
		return p.bmath
	end
}
p.qmath = {
	abs=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		if imag == 0 and jpart == 0 and kpart == 0 then return math.abs(real) end
		return math.sqrt( real * real + imag * imag + jpart * jpart + kpart * kpart )
	end,
	floor=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		return p.qmath.getQuaternionNumber(math.floor(real), math.floor(imag), math.floor(jpart), math.floor(kpart))
	end,
	ceil=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		return p.qmath.getQuaternionNumber(math.ceil(real), math.ceil(imag), math.ceil(jpart), math.ceil(kpart))
	end,
	div=function(left,z)
		local lreal, limag, ljpart, lkpart = p.qmath.readPart(left)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		return p.qmath.getQuaternionNumber(lreal, limag, ljpart, lkpart) * (p.qmath.getQuaternionNumber( real, -imag, -jpart, -kpart ) / ( real*real + imag*imag + jpart*jpart + kpart*kpart ))
	end,
	round=function(op1,op2,op3)
		local number = p.qmath.getQuaternionNumber(tonumber(op1) or op1.real or 0, (tonumber(op1) and 0) or (op1.imag or 0) or 0, (tonumber(op1) and 0) or (op1.jpart or 0) or 0, (tonumber(op1) and 0) or (op1.kpart or 0) or 0) 
		local digs = p.qmath.getQuaternionNumber(tonumber(op2) or (op2 or {}).real or 0, (tonumber(op2) and 0) or ((op2 or {}).imag or 0) or 0, (tonumber(op2) and 0) or ((op2 or {}).jpart or 0) or 0, (tonumber(op2) and 0) or ((op2 or {}).kpart or 0) or 0 ) 
		local base = p.qmath.getQuaternionNumber(tonumber(op3) or (op3 or {}).real or 10, (tonumber(op3) and 0) or ((op3 or {}).imag or 0) or 0, (tonumber(op3) and 0) or ((op3 or {}).jpart or 0) or 0, (tonumber(op3) and 0) or ((op3 or {}).kpart or 0) or 0 ) 
		local round_rad = p.qmath.pow(base,digs)
		local check_number = number * round_rad
		check_number.real = check_number.real + 0.5; check_number.imag = check_number.imag + 0.5; 
		check_number.jpart = check_number.jpart + 0.5; check_number.kpart = check_number.kpart + 0.5; 
		return p.qmath.floor( check_number ) * p.qmath.inverse(round_rad)
	end,
	re=function(z)return tonumber(z) or z.real end,
	im=function(z) return (tonumber(z) and 0) or z.imag end,
	conjugate=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		return p.qmath.getQuaternionNumber( real, -imag, -jpart, -kpart )
	end,
	inverse=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		return p.qmath.getQuaternionNumber( real, -imag, -jpart, -kpart ) / ( real*real + imag*imag + jpart*jpart + kpart*kpart )
	end,
	tovector=function(z)
		return {p.qmath.readPart(z)}
	end,
	trunc=function(z,digs)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		local n = tonumber(digs) or digs.real or 0
		return p.qmath.getQuaternionNumber( sollib._trunc(real,n), sollib._trunc(imag,n), sollib._trunc(jpart,n), sollib._trunc(kpart,n) )
	end,
	sqrt=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		if jpart == 0 and kpart == 0 then 
			local complex = p.cmath.sqrt(p.cmath.getComplexNumber(real, imag))
			return p.qmath.getQuaternionNumber(complex.real, complex.imag, 0, 0)
		end
		return p.qmath.pow(z, 0.5)
	end,
	root=function(_z,_n,_num)
		local z = p.qmath.getQuaternionNumber(p.qmath.readPart(_z))
		local n = p.qmath.getQuaternionNumber(p.qmath.readPart(_n or 2))
		local num = p.qmath.getQuaternionNumber(p.qmath.readPart(_num or 1))
		if num == p.qmath.one or num == p.qmath.zero or num == nil then
			return p.qmath.pow(z, p.qmath.inverse(n))
		end
		local sgn_data = p.qmath.sgn(p.qmath.nonRealPart(z))
		if math.abs(sgn_data.imag)<1e-14 and math.abs(sgn_data.jpart)<1e-14 and math.abs(sgn_data.kpart)<1e-14 then sgn_data=p.qmath.getQuaternionNumber(0,1,0,0) end
		local result = p.qmath.pow(p.qmath.abs(z), p.qmath.inverse(n)) * p.qmath.exp(sgn_data * (p.qmath.arg(z) + (num-1)*(2*math.pi) ) * p.qmath.inverse(n))
		result:clean()
		return result
	end,
	sin=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		local u = p.qmath.getQuaternionNumber(0, imag, jpart, kpart)
		return ( math.cosh(p.qmath.abs(u)) * math.sin(real) ) + ( p.qmath.sgn(u) * math.sinh(p.qmath.abs(u)) * math.cos(real) )
	end,
	cos=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		local u = p.qmath.getQuaternionNumber(0, imag, jpart, kpart)
		return ( math.cosh(p.qmath.abs(u)) * math.cos(real) ) - ( p.qmath.sgn(u) * math.sinh(p.qmath.abs(u)) * math.sin(real) )
	end,
	tan=function(z)
		local theta = p.qmath.readComplexNumber(z)
		return p.qmath.sin(theta) * p.qmath.inverse( p.qmath.cos(theta) )
	end,
	cot=function(z)
		local theta = p.qmath.readComplexNumber(z)
		return p.qmath.cos(theta) * p.qmath.inverse( p.qmath.sin(theta) )
	end,

	asin=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		local u, v = p.qmath.getQuaternionNumber(0, imag, jpart, kpart), p.qmath.getQuaternionNumber(real, imag, jpart, kpart)
		local sgnu = p.qmath.sgn(u); 
		if math.abs(sgnu.imag) < 1e-12 and math.abs(sgnu.jpart) < 1e-12 and math.abs(sgnu.kpart) < 1e-12 then sgnu.imag = 1 end
		return -sgnu * p.qmath.asinh( v * sgnu )
	end,
	acos=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		local u, v = p.qmath.getQuaternionNumber(0, imag, jpart, kpart), p.qmath.getQuaternionNumber(real, imag, jpart, kpart)
		local sgnu = p.qmath.sgn(u); 
		if math.abs(sgnu.imag) < 1e-12 and math.abs(sgnu.jpart) < 1e-12 and math.abs(sgnu.kpart) < 1e-12 then sgnu.imag = 1 end
		return -sgnu * p.qmath.acosh( v )
	end,
	atan=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		local u, v = p.qmath.getQuaternionNumber(0, imag, jpart, kpart), p.qmath.getQuaternionNumber(real, imag, jpart, kpart)
		local sgnu = p.qmath.sgn(u); 
		if math.abs(sgnu.imag) < 1e-12 and math.abs(sgnu.jpart) < 1e-12 and math.abs(sgnu.kpart) < 1e-12 then sgnu.imag = 1 end
		return -sgnu * p.qmath.atanh( v * sgnu )
	end,
	acot=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		local u, v = p.qmath.getQuaternionNumber(0, imag, jpart, kpart), p.qmath.getQuaternionNumber(real, imag, jpart, kpart)
		local sgnu = p.qmath.sgn(u); 
		if math.abs(sgnu.imag) < 1e-12 and math.abs(sgnu.jpart) < 1e-12 and math.abs(sgnu.kpart) < 1e-12 then sgnu.imag = 1 end
		return sgnu * p.qmath.acoth( v * sgnu )
	end,

	sinh=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		local u = p.qmath.getQuaternionNumber(0, imag, jpart, kpart)
		return ( math.cos(p.qmath.abs(u)) * math.sinh(real) ) + ( p.qmath.sgn(u) * math.sin(p.qmath.abs(u)) * math.cosh(real) )
	end,
	cosh=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		local u, v = p.qmath.getQuaternionNumber(0, imag, jpart, kpart), p.qmath.getQuaternionNumber(real, imag, jpart, kpart)
		return ( math.cos(p.qmath.abs(u)) * math.cosh(real) ) + ( p.qmath.sgn(u) * math.sin(p.qmath.abs(u)) * math.sinh(real) )
	end,
	tanh=function(z)
		local theta = p.qmath.readComplexNumber(z)
		return p.qmath.sinh(theta) * p.qmath.inverse( p.qmath.cosh(theta) )
	end,
	coth=function(z)
		local theta = p.qmath.readComplexNumber(z)
		return p.qmath.cosh(theta) * p.qmath.inverse( p.qmath.sinh(theta) )
	end,

	asinh=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		local u = p.qmath.getQuaternionNumber(real, imag, jpart, kpart)
		return p.qmath.log( u + p.qmath.sqrt( u * u + p.qmath.getQuaternionNumber(1,0,0,0) ) )
	end,
	acosh=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		local u = p.qmath.getQuaternionNumber(real, imag, jpart, kpart)
		return p.qmath.log( u + p.qmath.sqrt( u + p.qmath.getQuaternionNumber(1,0,0,0) ) * p.qmath.sqrt( u + p.qmath.getQuaternionNumber(-1,0,0,0) ) )
	end,
	atanh=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		local u = p.qmath.getQuaternionNumber(real, imag, jpart, kpart)
		return ( p.qmath.log( 1 + u ) - p.qmath.log( 1 - u ) ) / 2
	end,
	acoth=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		local u = p.qmath.getQuaternionNumber(real, imag, jpart, kpart)
		return ( p.qmath.log( 1 + p.qmath.inverse(u) ) - p.qmath.log( 1 - p.qmath.inverse(u) ) ) / 2
	end,

	dot = function (op1, op2) 
		local a, t = tonumber(op1) or op1.real, tonumber(op2) or op2.real
		local b, x = (tonumber(op1) and 0) or op1.imag, (tonumber(op2) and 0) or op2.imag
		local c, y = (tonumber(op1) and 0) or (op1.jpart or 0), (tonumber(op2) and 0) or (op2.jpart or 0)
		local d, z = (tonumber(op1) and 0) or (op1.kpart or 0), (tonumber(op2) and 0) or (op2.kpart or 0)
		return a * t + b * x + c * y + d * z
	end,
	outer = function (op1, op2) 
		local a, t = tonumber(op1) or op1.real, tonumber(op2) or op2.real
		local b, x = (tonumber(op1) and 0) or op1.imag, (tonumber(op2) and 0) or op2.imag
		local c, y = (tonumber(op1) and 0) or (op1.jpart or 0), (tonumber(op2) and 0) or (op2.jpart or 0)
		local d, z = (tonumber(op1) and 0) or (op1.kpart or 0), (tonumber(op2) and 0) or (op2.kpart or 0)
		return p.qmath.getQuaternionNumber(0,
			c*z-d*y,
			d*x-b*z,
			b*y-x*c
		)
	end,

	scalarPartQuaternion=function(z)
		return p.qmath.getQuaternionNumber(tonumber(z) or z.real, 0, 0, 0)
	end,
	nonRealPart=function(z) return p.qmath.getQuaternionNumber(0, (tonumber(z) and 0) or (z.imag or 0), (tonumber(z) and 0) or (z.jpart or 0), (tonumber(z) and 0) or (z.kpart or 0)) end,
	vectorPartQuaternion=function(z)
		return p.qmath.getQuaternionNumber(0, (tonumber(z) and 0) or (z.imag or 0), (tonumber(z) and 0) or (z.jpart or 0), (tonumber(z) and 0) or (z.kpart or 0))
	end,
	sgn=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		local length = math.sqrt( real * real + imag * imag + jpart * jpart + kpart * kpart )
		if length <= 0 then return p.qmath.getQuaternionNumber(0,0,0,0) end
		return z / length
	end,
	arg=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		local length = math.sqrt( real * real + imag * imag + jpart * jpart + kpart * kpart )
		return math.acos( real / length )
	end,
	cis=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		local u = p.qmath.getQuaternionNumber(real, imag, jpart, kpart)
		return p.qmath.cos(u) + p.qmath.getQuaternionNumber(0,1,0,0) * p.qmath.sin(u)
	end,
	exp=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		local u = p.qmath.getQuaternionNumber(0, imag, jpart, kpart)
		return ( (p.qmath.sgn(u) * math.sin(p.qmath.abs(u))) + math.cos(p.qmath.abs(u))) * math.exp(real)
	end,
	elog=function(z)
		local real, imag, jpart, kpart = p.qmath.readPart(z)
		local u, v = p.qmath.getQuaternionNumber(0, imag, jpart, kpart), p.qmath.getQuaternionNumber(real, imag, jpart, kpart)
		return (p.qmath.sgn(u) * p.qmath.arg(v)) + math.log(p.qmath.abs(v))
	end,
	log=function(z,basez)
		if basez~=nil then return p.qmath.elog(basez) * p.qmath.inverse(p.qmath.elog(z)) end
		return p.qmath.elog(z)
	end,
	pow=function(op1,op2)
		local check_op1, check_op2 = tonumber(tostring(op1)) or -1, tonumber(tostring(op2)) or -1
		if check_op1 == 1 then return p.qmath.getQuaternionNumber(1,0,0,0) end -- 1^z === 1
		if check_op2 == 1 then return op1 end -- z^1 === z
		if check_op2 == 0 then -- z^0
			if check_op1 ~= 0 then return p.qmath.getQuaternionNumber(1,0,0,0) -- z^0 === 1, z ≠ 0
			else return p.qmath.getQuaternionNumber(tonumber('nan'),0,0,0) end --0^0 Indeterminate
		elseif check_op1 == 0 then 
			if check_op2 < 0 then return p.qmath.getQuaternionNumber(tonumber('inf'),0,0,0) end -- 0^(-n) Infinity
			return p.qmath.getQuaternionNumber(0,0,0,0) -- 0^z === 0, z ≠ 0
		end
		--a ^ z
		local a = p.qmath.getQuaternionNumber( p.qmath.readPart(op1) )
		local z = p.qmath.getQuaternionNumber( p.qmath.readPart(op2) )
		a:clean();z:clean();
		if a.jpart == 0 and z.jpart == 0 and a.kpart == 0 and z.kpart == 0 then 
			local complex = p.cmath.pow(p.cmath.getComplexNumber(a.real, a.imag), p.cmath.getComplexNumber(z.real, z.imag))
			return p.qmath.getQuaternionNumber(complex.real, complex.imag, 0, 0)
		end
		return p.qmath.exp(z * p.qmath.log(a)):clean()
	end,

	random = function (op1, op2)
		if type(op1)==type(nil) and type(op2)==type(nil) then return p.qmath.getQuaternionNumber(math.random(), 0, 0, 0) end
		local a, t = tonumber(op1) or (op1 or {}).real or 0, tonumber(op2) or (op2 or{}).real or 0
		local b, x = (tonumber(op1) and 0) or (op1 or {}).imag or 0, (tonumber(op2) and 0) or (op2 or{}).imag or 0
		local c, y = (tonumber(op1) and 0) or ((op1 or {}).jpart or 0) or 0, (tonumber(op2) and 0) or ((op2 or{}).jpart or 0)
		local d, z = (tonumber(op1) and 0) or ((op1 or {}).kpart or 0) or 0, (tonumber(op2) and 0) or ((op2 or{}).kpart or 0)
		if type(op2)==type(nil) then return p.qmath.getQuaternionNumber(sollib._random(a), sollib._random(b), sollib._random(c), sollib._random(d)) end
		return p.qmath.getQuaternionNumber(sollib._random(math.min(a, t), math.max(a, t)), sollib._random(math.min(b, x), math.max(b, x)), sollib._random(math.min(c, y), math.max(c, y)), sollib._random(math.min(d, z), math.max(d, z)))
	end,

	isReal=function(z) return p.qmath.abs(p.qmath.nonRealPart(z)) < 1e-14 end,
	
	QuaternionNumberMeta = {
		__add = function (op1, op2) 
			local a, t = tonumber(op1) or (op1 or {}).real, tonumber(op2) or (op2 or {}).real
			local b, x = (tonumber(op1) and 0) or (op1 or {}).imag, (tonumber(op2) and 0) or (op2 or {}).imag
			local c, y = (tonumber(op1) and 0) or ((op1 or {}).jpart or 0), (tonumber(op2) and 0) or ((op2 or {}).jpart or 0)
			local d, z = (tonumber(op1) and 0) or ((op1 or {}).kpart or 0), (tonumber(op2) and 0) or ((op2 or {}).kpart or 0)
			return p.qmath.getQuaternionNumber(a + t, b + x, c + y, d + z) 
		end,
		__sub = function (op1, op2) 
			local a, t = tonumber(op1) or (op1 or {}).real, tonumber(op2) or (op2 or {}).real
			local b, x = (tonumber(op1) and 0) or (op1 or {}).imag, (tonumber(op2) and 0) or (op2 or {}).imag
			local c, y = (tonumber(op1) and 0) or ((op1 or {}).jpart or 0), (tonumber(op2) and 0) or ((op2 or {}).jpart or 0)
			local d, z = (tonumber(op1) and 0) or ((op1 or {}).kpart or 0), (tonumber(op2) and 0) or ((op2 or {}).kpart or 0)
			return p.qmath.getQuaternionNumber(a - t, b - x, c - y, d - z) 
		end,
		__mul = function (op1, op2) 
			local a1, a2 = tonumber(op1) or (op1 or {}).real, tonumber(op2) or (op2 or {}).real
			local b1, b2 = (tonumber(op1) and 0) or (op1 or {}).imag, (tonumber(op2) and 0) or (op2 or {}).imag
			local c1, c2 = (tonumber(op1) and 0) or ((op1 or {}).jpart or 0), (tonumber(op2) and 0) or ((op2 or {}).jpart or 0)
			local d1, d2 = (tonumber(op1) and 0) or ((op1 or {}).kpart or 0), (tonumber(op2) and 0) or ((op2 or {}).kpart or 0)
			return p.qmath.getQuaternionNumber(
				a1 * a2 - b1 * b2 - c1 * c2 - d1 * d2, 
				a1 * b2 + b1 * a2 + c1 * d2 - d1 * c2,
				a1 * c2 - b1 * d2 + c1 * a2 + d1 * b2, 
				a1 * d2 + b1 * c2 - c1 * b2 + d1 * a2
			) 
		end,
		__div = function (op1, op2) 
			local r1, r2 = tonumber(op1) or (op1 or {}).real, tonumber(op2) or (op2 or {}).real
			local i1, i2 = (tonumber(op1) and 0) or (op1 or {}).imag, (tonumber(op2) and 0) or (op2 or {}).imag
			local j1, j2 = (tonumber(op1) and 0) or ((op1 or {}).jpart or 0), (tonumber(op2) and 0) or ((op2 or {}).jpart or 0)
			local k1, k2 = (tonumber(op1) and 0) or ((op1 or {}).kpart or 0), (tonumber(op2) and 0) or ((op2 or {}).kpart or 0)
			if i2 ~= 0 or j2 ~= 0 or k2 ~= 0 then error( "Quaternion can not divide by non scalar value" ) end
			local op1_d, op2_d = r1*r1 + i1*i1 + j1*j1 + k1*k1, r2*r2 + i2*i2 + j2*j2 + k2*k2
			if op2_d <= 0 then return op1_d / op2_d end
			return p.qmath.getQuaternionNumber(r1/r2, i1/r2, j1/r2, k1/r2) 
		end,
		__mod = function (op1, op2) 
			local x = p.qmath.getQuaternionNumber(tonumber(op1) or (op1 or {}).real, (tonumber(op1) and 0) or (op1 or {}).imag, (tonumber(op1) and 0) or ((op1 or {}).jpart or 0), (tonumber(op1) and 0) or ((op1 or {}).kpart or 0) )
			local y = p.qmath.getQuaternionNumber(tonumber(op2) or (op2 or {}).real, (tonumber(op2) and 0) or (op2 or {}).imag, (tonumber(op2) and 0) or ((op2 or {}).jpart or 0), (tonumber(op2) and 0) or ((op2 or {}).kpart or 0) ) 
			return x - y * p.qmath.floor(x / y) 
		end,
		__tostring = function (this) 
			local body = ''
			if this.real ~= 0 then body = tostring(this.real) end
			if this.imag ~= 0 then 
				if body ~= '' and this.imag > 0 then body = body .. '+' end
				if this.imag == -1 then  body = body .. '-' end
				if math.abs(this.imag) ~= 1 then body = body .. tostring(this.imag) end
				body = body .. 'i'
			end
			if this.jpart ~= 0 then 
				if body ~= '' and this.jpart > 0 then body = body .. '+' end
				if this.jpart == -1 then  body = body .. '-' end
				if math.abs(this.jpart) ~= 1 then body = body .. tostring(this.jpart) end
				body = body .. 'j'
			end
			if this.kpart ~= 0 then 
				if body ~= '' and this.kpart > 0 then body = body .. '+' end
				if this.kpart == -1 then  body = body .. '-' end
				if math.abs(this.kpart) ~= 1 then body = body .. tostring(this.kpart) end
				body = body .. 'k'
			end
			if sollib._isNaN(this.real) or sollib._isNaN(this.imag) or sollib._isNaN(this.jpart) or sollib._isNaN(this.kpart) then body = 'nan' end
			if body == '' then body = '0' end
			return body
		end,
		__unm = function (this)
			return p.qmath.getQuaternionNumber(-this.real, -this.imag, -this.jpart, -this.kpart) 
		end,
		__eq = function (op1, op2)
			local diff_real = math.abs( (tonumber(op1) or (op1 or {}).real) - (tonumber(op2) or (op2 or {}).real) )
			local diff_imag1 = math.abs( ( (tonumber(op1) and 0) or (op1 or {}).imag) - ( (tonumber(op2) and 0) or (op2 or {}).imag) )
			local diff_jpart = math.abs( ( (tonumber(op1) and 0) or ((op1 or {}).jpart or 0)) - ( (tonumber(op2) and 0) or ((op2 or {}).jpart or 0)) )
			local diff_kpart = math.abs( ( (tonumber(op1) and 0) or ((op1 or {}).kpart or 0)) - ( (tonumber(op2) and 0) or ((op2 or {}).kpart or 0)) )
			return diff_real < 1e-12 and diff_imag1 < 1e-12 and diff_jpart < 1e-12 and diff_kpart < 1e-12
		end,
	},
	ele=function(id)
		local _zero = p.qmath.getQuaternionNumber(0, 0, 0, 0)
		local eles = (p.qmath.elements or {})
		local id_msg = tonumber(tostring(id)) or 0
		return eles[id_msg+1]or _zero
	end,
	readComplexNumber = function(z)
		if type(z) == type({}) then --if already be complex number, don't run string find.
			if z.numberType == "complex" then
				return p.qmath.getQuaternionNumber(z.real, z.imag, 0, 0)
			elseif z.numberType == "quaternion" then
				return z
			end
		elseif type(z) == type(0) then
			return p.qmath.getQuaternionNumber(z, 0, 0, 0)
		elseif type(z) == type(true) then
			return p.qmath.getQuaternionNumber(z and 1 or 0, 0, 0, 0)
		end
		return p.qmath.getQuaternionNumber(tonumber(z) or (z or {}).real or tonumber(tostring(z)) or 0, 
			((tonumber(z) or tonumber(tostring(z))) and 0) or ((z or {}).imag or 0), 
			((tonumber(z) or tonumber(tostring(z))) and 0) or ((z or {}).jpart or 0), 
			((tonumber(z) or tonumber(tostring(z))) and 0) or ((z or {}).kpart or 0)) 
	end,
	readPart = function(z)
		if type(z) == type({}) and (z.numberType == "complex" or z.numberType == "quaternion") then --if already be complex number, don't run string find.
			if z.numberType == "quaternion"then
				return z.real, z.imag, z.jpart, z.kpart
			else
				return z.real, z.imag, 0, 0
			end
		elseif type(z) == type(0) then
			return z, 0, 0, 0
		elseif type(z) == type(true) then
			return z and 1 or 0, 0, 0, 0
		end
		return tonumber(z) or (z or {}).real or tonumber(tostring(z)) or 0, 
			((tonumber(z) or tonumber(tostring(z))) and 0) or ((z or {}).imag or 0), 
			((tonumber(z) or tonumber(tostring(z))) and 0) or ((z or {}).jpart or 0), 
			((tonumber(z) or tonumber(tostring(z))) and 0) or ((z or {}).kpart or 0)
	end,
	getQuaternionNumber = function(real, imag, jpart, kpart)
		local QuaternionNumber = {}
		setmetatable(QuaternionNumber,p.qmath.QuaternionNumberMeta) 
		function QuaternionNumber:update()
			self.argument = 0
			self.length = math.sqrt( self.real * self.real + self.imag * self.imag
				+ self.jpart * self.jpart + self.kpart * self.kpart )
		end
		function QuaternionNumber:clean()
			if math.abs(self.real) <= 1e-12 then self.real = 0 end
			if math.abs(self.imag) <= 1e-12 then self.imag = 0 end
			if math.abs(self.jpart) <= 1e-12 then self.jpart = 0 end
			if math.abs(self.kpart) <= 1e-12 then self.kpart = 0 end
			if math.abs(self.real - math.floor(self.real)) <= 1e-12 then self.real = math.floor(self.real) end
			if math.abs(self.imag - math.floor(self.imag)) <= 1e-12 then self.imag = math.floor(self.imag) end
			if math.abs(self.jpart - math.floor(self.jpart)) <= 1e-12 then self.jpart = math.floor(self.jpart) end
			if math.abs(self.kpart - math.floor(self.kpart)) <= 1e-12 then self.kpart = math.floor(self.kpart) end
			return self
		end
		QuaternionNumber.real, QuaternionNumber.imag, QuaternionNumber.jpart, QuaternionNumber.kpart = real, imag, jpart, kpart
		QuaternionNumber.numberType = "quaternion"
		return QuaternionNumber
	end,
	toQuaternionNumber = function(num_str)
		local real, imag, jpart, kpart
		if num_str == nil then return nil end
		if type(num_str) == type({}) then --if already be complex number, don't run string find.
			if num_str.numberType == "quaternion" then
				return num_str 
			elseif num_str.numberType == "complex" then
				return p.qmath.getQuaternionNumber(num_str.real, num_str.imag, 0, 0)
			end
		elseif type(num_str) == type(1) then
			return p.qmath.getQuaternionNumber(num_str, 0, 0, 0)
		elseif type(num_str) == type(true) then
			return p.qmath.getQuaternionNumber(num_str and 1 or 0, 0, 0, 0)
		end
		if ( type(num_str)==type(0) or ( (type(num_str)==type({"table"})) and type(num_str.real)==type(0) ) ) then
			real, imag, jpart, kpart = tonumber(num_str) or num_str.real, (tonumber(num_str) and 0) or (num_str.imag or 0), (tonumber(num_str) and 0) or (num_str.jpart or 0), (tonumber(num_str) and 0) or (num_str.kpart or 0)
		else
			real, imag, jpart, kpart = p.qmath.toQuaternionNumberPart(tostring(num_str))
		end
		if real == nil or imag == nil or jpart == nil or kpart == nil then return nil end
		return p.qmath.getQuaternionNumber(real, imag, jpart, kpart)
	end,
	toQuaternionNumberPart = function(num_str)
		if type(num_str) == type(function()end) then return end
		if type(num_str) == type(true) then if num_str then return 1,0,0,0 else return 0,0,0,0 end end
		local body = ''
		local real, imag, jpart, kpart = 0, 0, 0, 0
		local split_str = mw.text.split(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(
				tostring(num_str) or '',
			'%s+',''),'%++([%d%.])',',+%1'),'%++([ijk])',',+1%1'),'%-+([%d%.])',',-%1'),'%-+([ijk])',',-1%1'),'%*+([%d%.])',',*%1'),'%*+([ijk])',',*1%1'),'%/+([%d%.])',',/%1'),'%/+([ijk])',',/1%1'),',')
		local first = true
		local continue = false
		
		for k,v in pairs(split_str) do
			continue = false
			local val = mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.text.trim(v),'^(%.)','0%1'),'^([%d%.])','+%1'),'([%+%-])([%d%.])','%1\48%2'),'^([ijk])','+1%1')
			if mw.ustring.find(val,"%/") or mw.ustring.find(val,"%*") then return end

			if val == nil or val == '' then if first == true then first = false continue = true else return end end
			if not continue then
				local num_text = mw.ustring.match(val,"[%+%-][%d%.]+[ijk]?")
				if num_text ~= val then return end
				local num_part = tonumber(mw.ustring.match(num_text,"[%+%-][%d%.]+"))
				if num_part == nil then return end
				if mw.ustring.find(num_text,"i") then
					imag = imag + num_part
				elseif mw.ustring.find(num_text,"j") then
					jpart = jpart + num_part
				elseif mw.ustring.find(num_text,"k") then
					kpart = kpart + num_part
				else
					real = real + num_part
				end
			end
		end
		return real, imag, jpart, kpart
	end,
	halfNumberParts = function(num)
		local real, imag, jpart, kpart = p.qmath.readPart(num)
		return {p.cmath.getComplexNumber(real, imag), p.cmath.getComplexNumber(jpart, kpart)}
	end,
	init = function()
		p.qmath.pi = p.qmath.getQuaternionNumber(math.pi, 0, 0, 0) 
		p.qmath["π"] = p.qmath.getQuaternionNumber(math.pi, 0, 0, 0)
		p.qmath["°"] = p.qmath.getQuaternionNumber(math.pi/180, 0, 0, 0)
		p.qmath.e = p.qmath.getQuaternionNumber(math.exp(1), 0, 0, 0)
		p.qmath.nan = p.qmath.getQuaternionNumber(tonumber("nan"), tonumber("nan"), tonumber("nan"), tonumber("nan")) 
		p.qmath.infi = p.qmath.getQuaternionNumber(0, tonumber("inf"), 0, 0) 
		p.qmath.infj = p.qmath.getQuaternionNumber(0, 0, tonumber("inf"), 0) 
		p.qmath.infk = p.qmath.getQuaternionNumber(0, 0, 0, tonumber("inf")) 
		p.qmath.zero = p.qmath.getQuaternionNumber(0, 0, 0, 0) 
		p.qmath.one = p.qmath.getQuaternionNumber(1, 0, 0, 0) 
		p.qmath[-1] = p.qmath.getQuaternionNumber(-1, 0, 0, 0) 
		p.qmath.i = p.qmath.getQuaternionNumber(0, 1, 0, 0) 
		p.qmath.j = p.qmath.getQuaternionNumber(0, 0, 1, 0)
		p.qmath.k = p.qmath.getQuaternionNumber(0, 0, 0, 1) 
		p.qmath[0],p.qmath[1] = p.qmath.zero,p.qmath.one
		p.qmath.numberType = sollib._numberType
		p.qmath.constructor = p.qmath.toQuaternionNumber
		p.qmath.elements = {
			p.qmath.getQuaternionNumber(1, 0, 0, 0),
			p.qmath.getQuaternionNumber(0, 1, 0, 0),
			p.qmath.getQuaternionNumber(0, 0, 1, 0),
			p.qmath.getQuaternionNumber(0, 0, 0, 1),
		}
		return p.qmath
	end
}
p._efloor=function(z)
	local real, imag = tonumber(z) or z[eReal], (tonumber(z) and 0) or z[eImag]
	return p._eisenstein_integer(math.floor(real), math.floor(imag)) 
end
p._eisenstein_meta={
	__add = function (op1, op2) 
		local real1, real2 = tonumber(op1) or op1[eReal], tonumber(op2) or op2[eReal]
		local imag1, imag2 = (tonumber(op1) and 0) or op1[eImag], (tonumber(op2) and 0) or op2[eImag]
		if not real2 or not imag2 then 
			local sqrt32, sqrt33 = math.sqrt(3)/2, 1/math.sqrt(3)
			local real3, imag3 = tonumber(op2) or op2.real, (tonumber(op2) and 0) or op2.imag
			real2, imag2 = real3+sqrt33*imag3, 2*sqrt33*imag3 
		end
		return p._eisenstein_integer(real1 + real2, imag1 + imag2) 
	end,
	__sub = function (op1, op2) 
		local real1, real2 = tonumber(op1) or op1[eReal], tonumber(op2) or op2[eReal]
		local imag1, imag2 = (tonumber(op1) and 0) or op1[eImag], (tonumber(op2) and 0) or op2[eImag]
		if not real2 or not imag2 then 
			local sqrt32, sqrt33 = math.sqrt(3)/2, 1/math.sqrt(3)
			local real3, imag3 = tonumber(op2) or op2.real, (tonumber(op2) and 0) or op2.imag
			real2, imag2 = real3+sqrt33*imag3, 2*sqrt33*imag3 
		end
		return p._eisenstein_integer(real1 - real2, imag1 - imag2) 
	end,
	__mul = function (op1, op2) 
		local a, c = tonumber(op1) or op1[eReal], tonumber(op2) or op2[eReal]
		local b, d = (tonumber(op1) and 0) or op1[eImag], (tonumber(op2) and 0) or op2[eImag]
		if not c or not d then 
			local sqrt32, sqrt33 = math.sqrt(3)/2, 1/math.sqrt(3)
			local real3, imag3 = tonumber(op2) or op2.real, (tonumber(op2) and 0) or op2.imag
			c, d = real3+sqrt33*imag3, 2*sqrt33*imag3 
		end
		return p._eisenstein_integer(a * c - b * d, b * c + a * d - b * d) 
	end,
	__div = function (op1, op2) 
		local a, c = tonumber(op1) or op1[eReal], tonumber(op2) or op2[eReal]
		local b, d = (tonumber(op1) and 0) or op1[eImag], (tonumber(op2) and 0) or op2[eImag]
		if not c or not d then 
			local sqrt32, sqrt33 = math.sqrt(3)/2, 1/math.sqrt(3)
			local real3, imag3 = tonumber(op2) or op2.real, (tonumber(op2) and 0) or op2.imag
			c, d = real3+sqrt33*imag3, 2*sqrt33*imag3 
		end
		if c==d or c*c == (c*d*d)/(c-d) then
			local sqrt32, sqrt33 = math.sqrt(3)/2, 1/math.sqrt(3)
			local pn, q = p.cmath.getComplexNumber(a-b/2, sqrt32 * b), p.cmath.getComplexNumber(c-d/2, sqrt32 * d)
			local p_q = pn/q
			local real1, imag1 = tonumber(p_q) or p_q.real, (tonumber(p_q) and 0) or p_q.imag
			return p._eisenstein_integer(real1+sqrt33*imag1, 2*sqrt33*imag1)
		end
		local op1_d, op2_d = c*d/(c-d), c*c + (c*d*d)/(c-d)
		return p._eisenstein_integer((a * c + b * op1_d) / op2_d, (b * c - a * op1_d + b * op1_d) / op2_d)
	end,
	__mod = function (op1, op2) 
		local real1, real2 = tonumber(op1) or op1[eReal], tonumber(op2) or op2[eReal]
		local imag1, imag2 = (tonumber(op1) and 0) or op1[eImag], (tonumber(op2) and 0) or op2[eImag]
		if not real2 or not imag2 then 
			local sqrt32, sqrt33 = math.sqrt(3)/2, 1/math.sqrt(3)
			local real3, imag3 = tonumber(op2) or op2.real, (tonumber(op2) and 0) or op2.imag
			real2, imag2 = real3+sqrt33*imag3, 2*sqrt33*imag3 
		end
		local x = p._eisenstein_integer(real1, imag1)
		local y = p._eisenstein_integer(real2, imag2) 
		return x - y * p._efloor(x / y) 
	end,
		__tostring = function (this) 
			local body = ''
			if this[eReal] ~= 0 then body = tostring(this[eReal]) end
			if this[eImag] ~= 0 then 
				if body ~= '' and this[eImag] > 0 then body = body .. '+' end
				if this[eImag] == -1 then  body = body .. '-' end
				if math.abs(this[eImag]) ~= 1 then body = body .. tostring(this[eImag]) end
				body = body .. eImag
			end
			if body == '' then body = '0' end
			return body
		end,
		__unm = function (this)
			return p._eisenstein_integer(-this[eReal], -this[eImag]) 
		end,
		__eq = function (op1, op2)
			local real1, real2 = tonumber(op1) or op1[eReal], tonumber(op2) or op2[eReal]
			local imag1, imag2 = (tonumber(op1) and 0) or op1[eImag], (tonumber(op2) and 0) or op2[eImag]
			if not real2 or not imag2 then 
				local sqrt32, sqrt33 = math.sqrt(3)/2, 1/math.sqrt(3)
				local real3, imag3 = tonumber(op2) or op2.real, (tonumber(op2) and 0) or op2.imag
				real2, imag2 = real3+sqrt33*imag3, 2*sqrt33*imag3 
			end
			local diff_real = math.abs( real1 - real2 )
			local diff_imag1 = math.abs( imag1 - imag2 )
			return diff_real < 1e-12 and diff_imag1 < 1e-12
		end,
}

function p._eisenstein_integer(int_a, int_b)
	local sqrt32, sqrt33 = math.sqrt(3)/2, 1/math.sqrt(3)
	local eisenstein = p.cmath.getComplexNumber(int_a-int_b/2, sqrt32 * int_b)
	eisenstein[eReal], eisenstein[eImag] = int_a,int_b
	setmetatable(eisenstein,p._eisenstein_meta)
	eisenstein.isEisensteinNumber = true
	return eisenstein
end
function p._toEisensteinNumber(num_str)
	local real, imag
	if num_str == nil then return nil end
	if ( type(num_str)==type(0) or ( (type(num_str)==type({"table"})) and type(num_str.real)==type(0) ) ) then
		real, imag = tonumber(num_str) or num_str.real, (tonumber(num_str) and 0) or num_str.imag
	else
		real, imag = p._toEisensteinNumberPart(num_str)
	end
	if real == nil or imag == nil then return nil end
	return p._eisenstein_integer(real, imag)
end
function p._toEisensteinNumberPart(num_str)
	if type(num_str) == type(function()end) then return end
	local body = ''
	local real, imag, omg = 0, 0, 0
	local split_str = mw.text.split(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(
			tostring(num_str) or '',
		'%s+',''),'%++([%d%.])',',+%1'),'%++([ijω])',',+1%1'),'%-+([%d%.])',',-%1'),'%-+([ijω])',',-1%1'),'%*+([%d%.])',',*%1'),'%*+([ijω])',',*1%1'),'%/+([%d%.])',',/%1'),'%/+([ijω])',',/1%1'),',')
	local first = true
	local continue = false
	
	for k,v in pairs(split_str) do
		continue = false
		local val = mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.text.trim(v),'[ijω]+','i'),'^(%.)','0%1'),'^([%d%.])','+%1'),'([%+%-])([%d%.])','%1\48%2'),'^([ijω])','+1%1')
		if mw.ustring.find(val,"%/") or mw.ustring.find(val,"%*") then return end
		if val == nil or val == '' then if first == true then first = false continue = true else return end end
		if not continue then
			local num_text = mw.ustring.match(val,"[%+%-][%d%.]+i?")
			if num_text ~= val then return end
			local num_part = tonumber(mw.ustring.match(num_text,"[%+%-][%d%.]+"))
			if num_part == nil then return end
			if mw.ustring.find(num_text,"i") then
				imag = imag + num_part
			elseif mw.ustring.find(num_text,"ω") then
				omg = omg + num_part
			else
				real = real + num_part
			end
		end
	end
	local sqrt32, sqrt33 = math.sqrt(3)/2, 1/math.sqrt(3)
	return real+sqrt33*imag, 2*sqrt33*imag+omg
end

return p