Ancestors and Descendants Counts by Generation.fh_lua

--[[
@Title: Ancestors and Descendants - Counts by Generation
@Author: Jane Taubman
@Version: 1.3
@LastUpdated: 15 Dec 2020
@Description: Report of the number of Grandparents recorded for an individual by Generation and the 
number of direct decendants by Generation
1.1 Lua 5.3 Compatibility
1.2 V7 compatibility
1.3 Compute percentage and round before adding for V7

]]

function main()
    tblIndi = fhPromptUserForRecordSel('INDI',1)
    if #tblIndi == 0 then
        return
    end
    local pRoot = tblIndi[1]
    local iAncMax,iDecMax = 0,0
    local tblAncGen = {}
    local tblDecGen = {}
    local TblAncestorDescriptions = {'Parents','Grandparents','Great Grandparents'}
    local TblDescendantDescriptions = {'Child','Grandchildren','Great Grandchildren'}
    
    for pi in records('INDI') do
        local bAnc = fhCallBuiltInFunction('IsAncestorOf',pi,pRoot)
        if bAnc then
            -- Ancestor of selected person get number of Generations
            
            local iGens = fhCallBuiltInFunction('RelationCode',pi,pRoot,'GENS_DOWN',1)
            
            if tblAncGen[iGens] then tblAncGen[iGens] = tblAncGen[iGens] + 1
            else tblAncGen[iGens] = 1 end
            
            if iGens > iAncMax then iAncMax = iGens end
        end
        local bDec = fhCallBuiltInFunction('IsDescendantOf',pi,pRoot)
        if bDec then
            -- descendant of selected person get number of Generations
            
            local iGens = fhCallBuiltInFunction('RelationCode',pi,pRoot,'GENS_UP',1)
            
            print (fhGetDisplayText(pi),iGens)
            if tblDecGen[iGens] then tblDecGen[iGens] = tblDecGen[iGens] + 1
            else tblDecGen[iGens] = 1 end
            
            if iGens > iDecMax then iDecMax = iGens end
        end
    end
    ------------------------------------- Output Results
    local tblResults = createResultTable()
    -- Define Columns
    tblResults.gen = {title='Gen',type=integer,align='align_left',width=80,sort=1}
    tblResults.desc = {title='Description',type='Text',width=140}
    tblResults.poss1 = {title='Pos',type='integer',align='align_right',width=40}
    tblResults.posssum = {title='Pos Sum',type='integer',align='align_right',width=40}
    tblResults.actual = {title='Actual',type='integer',align='align_right',width=40}
    tblResults.actualsum = {title='Actual Sum',type='integer',align='align_right',width=40}
    tblResults.percent = {title='%',type='integer',align='align_right',width=40}
    
    tblResults:newRow()
    tblResults.gen:set(1)
    local sum,psum = 1,1
    tblResults.desc:set(fhGetDisplayText(pRoot))
    tblResults.poss1:set(1)
    tblResults.posssum:set(sum)
    tblResults.actual:set(1)
    tblResults.actualsum:set(1)
    tblResults.percent:set(100)
    for i = 1, iAncMax do
        tblResults:newRow()
        local sTitle = ''
        if i<4 then
            sTitle = TblAncestorDescriptions[i]
        else
            sTitle = (i - 2)..'x '..TblAncestorDescriptions[3]
        end
        tblAncGen[i] = tblAncGen[i] or 0
        
        tblResults.gen:set(i+1)
        tblResults.desc:set(sTitle)
        tblResults.poss1:set(2 ^i)
        psum = psum + 2^i
        sum = sum + tblAncGen[i]
        tblResults.posssum:set(psum)
        tblResults.actual:set(tblAncGen[i])
        tblResults.actualsum:set(sum)
		  local percent = math.floor((tblAncGen[i]/(2^i) * 100) + 0.5)
        tblResults.percent:set(percent)
        print (i,2 ^i,sTitle,tblAncGen[i] or 0)
    end
    sum = 0
    for i = 1, iDecMax do
        tblResults:newRow()
        local sTitle = ''
        if i<4 then
            sTitle = TblDescendantDescriptions[i]
        else
            sTitle = (i - 2)..'x '..TblDescendantDescriptions[3]
        end
        tblDecGen[i] = tblDecGen[i] or 0
        
        tblResults.gen:set((i+1) * -1)
        tblResults.desc:set(sTitle)
        tblResults.poss1:set(0)
        sum = sum + tblDecGen[i]
        tblResults.posssum:set(0)
        tblResults.actual:set(tblDecGen[i])
        tblResults.actualsum:set(sum)
        tblResults.percent:set(0)
        -- print (i,2 ^i,sTitle,tblDecGen[i] or 0)
    end
    local sTitle = "Ancestor and Descendant Breakdown for "..fhGetDisplayText(pRoot)
    fhOutputResultSetTitles(sTitle, sTitle, "Created Date: %#x")
    tblResults:outputResults()
end

function records(type)
    local pi = fhNewItemPtr()
    local p2 = fhNewItemPtr()
    pi:MoveToFirstRecord(type)
    return function ()
        p2:MoveTo(pi)
        pi:MoveNext()
        if p2:IsNotNull() then return p2 end
    end
end

function createResultTable()
    local tblOutput_mt = {} -- create metatable
    local iC = 0 -- Define count of lines
    local tblOutput = {} -- Define Columns Table
    tblOutput_mt.col = 0
    tblOutput_mt.seq = {}
    tblOutput_mt.__newindex =

    function (t,k,v)
        -- Set Values to Defaults if not supplied
        if v.content == nil then v.content = {} end
        if v.title == nil then v.title = k end
        if v.type == nil then v.type = 'notset' end
        if v.width == nil then v.width = 140 end
        if v.align == nil then v.align = 'align_left' end
        if v.sort == nil then v.sort = 0 end
        if v.sortAscending == nil then v.sortAscending = true end
        if v.sortType == nil then v.sortType = 'default' end
        if v.visibility == nil then v. visibility = 'show' end
        v.set =

        function(self,value)
            self.content[iC] = value
            if self.type == 'notset' then
                if type(value) == 'string' then
                    self.type = 'text'
                elseif type(value) == 'number' then
                    self.type = 'integer'
                    self.width = '30'
                else
                    self.type = 'item'
                end
            end
        end
        rawset(t,k,v) -- update original table
        local m = getmetatable(t)
        m.col = m.col + 1
        table.insert(m.seq,k)
    end
    tblOutput_mt.__call =

    function (t)
        local i = 0
        local m = getmetatable(t)
        local n = table.getn(m.seq)
        return function ()
            i = i + 1
            if i <= n then
                return t[m.seq[i]]
            end
        end
    end
    tblOutput.newRow = function(t)
        iC = iC + 1
    end
    tblOutput.rowCount = function(t)
        return iC
    end
    tblOutput.outputResults = function(self)
        for l in self() do
            fhOutputResultSetColumn(l.title, l.type, l.content, iC, l.width,l.align,l.sort,l.sortAscending,l.sortType,l.visibility )
        end
    end
    setmetatable(tblOutput, tblOutput_mt)
    return tblOutput
end
---------------------------------------------------------------- Lua 5.3
if not(table.getn) then
if fhGetAppVersion() > 6 then 
function table.getn(t)
local count = 0
for _, __ in pairs(t) do
count = count + 1
end
return count
end

function unpack(t)
    return table.unpack(t)
end
end

end
---------------------------------------------------------------- Run Main
main()

Source:Ancestors-and-Descendants-Counts-by-Generation-2.fh_lua