Code
--[[
===================================
* AA Copy Paste Compare
* Original Author: Brow42
* Version 0.5 Feb. 1 2012
* Easy way to copy / paste a new primary sequence or
* secondary structure assignment. Detects changes
* Version 1.0 Mar. 13 2012
* Bugfix: error exiting Write if Undo disabled
* Version 1.1 Aug. 4 2012
* Added a dialog for easy copying of compare results
* instead of using read dialog.
* Added checkbox to reverse initial/final for comparison
* Version 1.1.1 Sep. 22 2012
* Change detection was broken, syntax errors,
* if insert/delete were in the editlist
--]]
title = 'AA Copy Paste Compare v 1.1'
qsave_slot = nil -- set on first menu
qsave_err = 100
--beta_date = os.time({year=2012, month=3, day=15})
-- ================================== Begin Function Wrappers
-- These must always be at the top of a file
-- Remove the problematic molecule e.g. puzzle 471
structure._GetCount = structure.GetCount
function structure.HasMolecule()
return structure.GetSecondaryStructure(structure._GetCount())=='M'
end
function structure.GetCount()
local n = structure._GetCount()
while structure.GetSecondaryStructure(n) == 'M' do n = n - 1 end
return n
end
-- ================================== End Function Wrappers
-- ================================== Begin New Dialog Library Functions
-- Add a wall of text from a table
function dialog.AddLabels(d,msg,nlabels) -- need to manually pass in # of existing autolabels
nlabels = nlabels or 0
if type(msg) == 'string' then
msg = { msg }
end
for i = 1,#msg do
d['autolabel'..tostring(i+nlabels)] = dialog.AddLabel(msg[i])
end
end
-- Create but don't display a wall of text and 1 or 2 buttons
function dialog.CreateMessageBox(msg,title,buttontext1,buttontext0)
title = title or ''
local d = dialog.CreateDialog(title)
dialog.AddLabels(d,msg)
buttontext1 = buttontext1 or 'Ok'
d.button = dialog.AddButton(buttontext1,1)
if buttontext0 ~= nil then d.button0 = dialog.AddButton(buttontext0,0) end
return d
end
-- Display a dialog box
function dialog.ShowMessageBox(msg,title,buttontext1,buttontext0)
return dialog.Show(dialog.CreateMessageBox(msg,title,buttontext1,buttontext0))
end
-- Removes a control named 'control' created as d.control = dialog.AddControl()
function dialog.DeleteControl(d,control)
local err = string.format('No such control \'%s\'',control)
if d[control] == nil then error(err) end
d[control] = nil
for i = 1,#d._Order do
if d._Order[i] == control then
table.remove(d._Order,i) -- renumbers arrays
return
end
end
error(err)
end
-- ================================== End New Dialog Library Functions
-- ================================== Begin Dialogs
-- Create all the dialogs used by the program in table 'dialogs'
function CreateDialogs()
dialogs = {}
-- multipage help
local pages = {
{
'Primary and secondary structure can be',
'compressed or verbose.',
'',
'Verbose: a sequence of 1 letter codes representing',
'AAs(*) (primary) or EHL(**) (secondary). The range',
'field, if not blank, is the starting and ending AA to be',
'read, to only read part of the protein structure. When',
'setting the structure, provide the first AA to be',
'changed (if not 1).',
'',
'(*) AAs = ACDEFGHIKLMNPQRSTVWY',
'(**) HG = Helix; E = Sheet; IBST = Loop;',
' <space> = no-change, M = ignored'
},
{
'Compressed: For primary structure, you can just ',
'specify the AAs you want to change: "Y 10 K 11 P 20" ',
'would change 3 AAs, the rest is unchanged. For',
'secondary structure, everything in the range is option-',
'ally set to loop and then you specify 1 number to',
'change 1 AA or 2 numbers to change a range of AAs:',
'"E 10 H 15-20 E 23-27 33-38" would make a helix,',
'2 sheets, and an isolated sheet segment.'
},
{
'Undo: Your original structure is always available',
'in a user-selected quicksave slot. This can also be',
'loaded by hitting Undo All in the write menu.',
'If an error occurs while writing PS or SS, it',
'should undo those changes. If you perform a compare,',
'your structure before the Compare will be loaded',
'when you return to the Read menu.'
}
}
dialogs.about = {}
for i = 1,#pages do
local d = dialog.CreateDialog(title)
dialog.AddLabels(d,pages[i])
if i > 1 then d.back = dialog.AddButton('Previous',-1) end
if i < #pages then d.next = dialog.AddButton('Next',1) end
d.exit = dialog.AddButton('Return',0)
table.insert(dialogs.about,d)
end
-- Main dialog
local main = {
'This recipe will read the current primary and secondary',
'structure and present them so that you can copy them',
'to your clipboard.',
'You can also enter (paste) a primary or secondary',
'structure. and the recipe will change the protein to',
'match.'
}
dialogs.main = dialog.CreateDialog(title)
if beta_date then
dialogs.main.beta = dialog.AddLabel(string.format(
' * This is a BETA recipe that expires %s.',os.date('%b. %d %Y',beta_date)))
end
dialog.AddLabels(dialogs.main,main)
dialogs.main.undomsg1 = dialog.AddLabel('Pick a save slot now for Undo or 0 to disable Undo.')
dialogs.main.undomsg2 = dialog.AddLabel('(Use one that is unused, 1-3 good in case of cancel.)')
dialogs.main.slot = dialog.AddSlider('Save Slot:',1,0,10,0)
dialogs.main.read = dialog.AddButton('Read',1)
dialogs.main.write = dialog.AddButton('Write',2)
dialogs.main.help = dialog.AddButton('Help',3)
dialogs.main.exit = dialog.AddButton('Exit',0)
-- Read Dialog
local read = {
'Click Read to read the current PS & SS for export.',
'Enter a start segment or segment range (start-end)',
'to only read part of the protein.',
'Click Changes to compare against other structures',
'and find your changes.'
}
dialogs.read = dialog.CreateDialog('Export Structure')
dialog.AddLabels(dialogs.read,read)
dialogs.read.range = dialog.AddTextbox('Read Range:','')
dialogs.read.onlym = dialog.AddCheckbox('Mutable Only:',false)
dialogs.read.primary = dialog.AddTextbox('Primary S.:','')
dialogs.read.sec1 = dialog.AddTextbox('Secondary S.:','')
dialogs.read.sec2 = dialog.AddTextbox('Secondary S.:','')
dialogs.read.read = dialog.AddButton('Read',1)
dialogs.read.detect = dialog.AddButton('Changes',3)
dialogs.read.back = dialog.AddButton('Back',2)
-- Write Dialog
dialogs.write = dialog.CreateDialog('Import Structure')
dialogs.write.range = dialog.AddTextbox('Write Start Seg:','')
dialogs.write.primary = dialog.AddTextbox('Primary:','')
dialogs.write.setloops = dialog.AddCheckbox('Default to Loops:',false)
dialogs.write.sec1 = dialog.AddTextbox('Secondary S.:','')
dialogs.write.write = dialog.AddButton('Write',1)
dialogs.write.back = dialog.AddButton('Back',2)
dialogs.write.clear = dialog.AddButton('Clear',4)
dialogs.write.undo = dialog.AddButton('Undo All',3)
-- Changes Dialog
dialogs.change = dialog.CreateDialog('List Structure Changes')
local change = {
'\'Next Start\' will reset the puzzle. You may have to',
'loop through several to compare against the start',
'you want. \'This Start\' will automatically loop',
'through all starts.'
}
dialog.AddLabels(dialogs.change,change)
dialogs.change.label = dialog.AddLabel('Pick one compare method')
dialogs.change.next = dialog.AddCheckbox('Compare against NEXT start (one reset)',false)
dialogs.change.this = dialog.AddCheckbox('Compare against THIS start (by looping)',false)
dialogs.change.ss9 = dialog.AddCheckbox('Compare against Save SS slot 9',false)
dialogs.change.user = dialog.AddCheckbox('Compare against User slot #',false)
-- dialogs.change.ssst = dialog.user('Compare against ST SS Save',false)
dialogs.change.slot = dialog.AddSlider('',1,1,3,0)
dialogs.change.reverse = dialog.AddCheckbox('REVERSE the comparison',false)
dialogs.change.compare = dialog.AddButton('Compare',1)
dialogs.change.back = dialog.AddButton('Back',0)
-- Changes Result dialog
dialogs.diff = dialog.CreateDialog('Compare Results')
dialogs.diff.label = dialog.AddLabel('')
dialogs.diff.primary = dialog.AddTextbox('Comp. Pri','')
dialogs.diff.secondary = dialog.AddTextbox('Secondary','')
dialogs.diff.sscompressed = dialog.AddTextbox('Comp. Sec.','')
dialogs.diff.ok = dialog.AddButton('Okay',1)
end
-- ============================== End Dialogs
-- ============================== Begin Work Functions
-- Set the user-selected quicksave and delete uneeded controls
function SetQuicksave()
qsave_slot = dialogs.main.slot.value
dialog.DeleteControl(dialogs.main,'undomsg1')
dialog.DeleteControl(dialogs.main,'undomsg2')
if qsave_slot > 0 then
save.Quicksave(qsave_slot)
print('Saving to slot',qsave_slot)
dialogs.main.slot = dialog.AddLabel(string.format('Original structure available in slot #%d',qsave_slot))
else
print('No undo slot selected...undo disabled')
dialog.DeleteControl(dialogs.write,'undo')
dialogs.main.slot = dialog.AddLabel('Undo has been disabled.')
end
end
-- is there another way to do this? string.subs I guess
function string.at(str,i) return string.char(string.byte(str,i)) end
-- Recursive table printer (no cycles please)
function PrintTable(tab,indent)
indent = indent or ''
for i,v in pairs(tab) do
if type(v) == 'table' then do print(indent,i) PrintTable(v,indent..' ') end
else print(indent,i,v) end
end
end
-- Group up identical symbols into symbol,range table
function Compress(str,start)
local sym = ''
local count = 0
local result = {}
for i = 1,#str+1 do
if string.at(str,i) == sym then count = count + 1
else
if count > 0 then result[#result+1] = {sym,start+i-1-count,start+i-2} end
count = 1
sym = string.at(str,i)
end
end
return result
end
-- Turn compressed range string into table of ranges
-- The was more complicated than I'd envisioned
-- returns nil if error
function ParseCompressed(str)
-- First turn string into letter - number pairs
local init = 1
local tab = {}
local ranging = false -- true means range extension is disallowed
local lower = 0 -- lower value of range, 0 illegal
local sym = '' -- symbol being compressed, empty illegal
while init <= #str do
local a,b,c,d = string.find(str, ' *([%a%-]?) *(%d+) *',init)
if a == nil then break end
init = b+1
-- continuing a range, check if it's valid
if c == '-' then
if ranging == true then
dialog.ShowMessageBox('Multiple range numbers (a range is number - number)','Input Error')
return nil
end
if sym == '' or lower == 0 then
dialog.ShowMessageBox('Initial range (a range continues a structure number pair)','Input Error')
return nil
end
tab[#tab+1] = {sym, lower, tonumber(d)}
ranging = true
lower = 0
-- starting a new range with explicit symbol
elseif c ~= '' then
ranging = false
if sym ~= '' and lower > 0 then tab[#tab+1] = {sym,lower,lower} end
sym = c
lower = tonumber(d)
ranging = false
-- starting a new range with same symbol
elseif sym ~= '' then
if lower > 0 then tab[#tab+1] = {sym,lower,lower} end
lower = tonumber(d)
ranging = false
else
dialog.ShowMessageBox({'Missing structure symbol (a range starts',
'with a structure number pair'},'Input Error')
end
end
if lower > 0 then tab[#tab+1] = {sym,lower,lower} end
return tab
end
function PrintCompressed(tab,exclude)
exclude = exclude or {}
exclude[''] = true
local str,sep = '',''
for i = 1, #tab do
local x = tab[i]
if exclude[x[1]] == nil then
if x[3] ~= nil and x[3] > x[2] then
str = string.format('%s%s%s %d-%d',str,sep,x[1],x[2],x[3])
else
str = string.format('%s%s%s %d',str,sep,x[1],x[2])
end
sep = ' '
end
end
return str
end
-- Turn a compressed table into a string with space padding
function Uncompress(tab,exclude)
local tmp = {}
exclude = exclude or {}
exclude[''] = true
local max = 0
for i = 1, #tab do
local x = tab[i]
if exclude[x[1]] == nil then
for j = x[2],x[3] do tmp[j] = x[1] end
if x[3] > max then max = x[3] end
end
end
for i = 1,max do tmp[i] = tmp[i] or ' ' end
return table.concat(tmp,'')
end
-- Parse the user input for ranges, with error checking
function ParseRangeString(str)
local n = structure.GetCount()
local tmp = {}
for v in string.gfind(dialogs.read.range.value,'%d+') do tmp[#tmp+1] = tonumber(v) end
local first , last = tmp[1] or 1, tmp[2] or n
if first > last then first,last = last,first end
if last > n then
dialog.ShowMessageBox(string.format('Your range runs too high (max %d segments)',n),'Input Error')
last = n
end
if first < 1 then
dialog.ShowMessageBox('Your range runs too low (min is 1 of course)','Input Error')
first = 1
end
return first,last
end
-- Turn a string that may or may not be compressed into a symbol,range table
function StringToTable(str,startstr)
startstr = startstr or ''
local compressed = string.find(str,'%d') ~= nil
local tab
if compressed then
if startstr ~= '' then
dialog.ShowMessageBox({'Reminder: the start/range value is ignored','when using compressed notation.'},'Info')
end
tab = ParseCompressed(str)
if (tab == nil) then
dialog.ShowMessageBox('Error parsing compressed string.')
return nil
end
else
local start = 1
if startstr ~= '' then start = tonumber(startstr) end
tab = Compress(str,start)
end
return tab
end
-- Turn a string that may or may not be compressed into a symbol,range table
function StringToTable(str,startstr)
startstr = startstr or ''
local compressed = string.find(str,'%d') ~= nil
local tab
if compressed then
if startstr ~= '' then
dialog.ShowMessageBox({'Reminder: the start/range value is ignored','when using compressed notation.'},'Info')
end
tab = ParseCompressed(str)
if (tab == nil) then
dialog.ShowMessageBox('Error parsing compressed string.')
return nil
end
else
local start = 1
if startstr ~= '' then start = tonumber(startstr) end
tab = Compress(str,start)
end
return tab
end
-- returns Hamming distance and a sparse array of deltas containing only substitutions
function string.hamming(initial,final)
if #initial ~= #final then error('Strings of different length passed to hamming()') end
local deltas = {}
for i = 0,#initial do deltas[i] = {} end
local changes = 0
deltas[0][0] = changes
for i = 1,#final do
if initial[i] ~= final[i] then changes = changes + 1 end
deltas[i][i] = changes
end
return changes,deltas
end
function PrintDeltaTable(initial,final,deltas)
local line = ' . .'
for i = 1,#final do line = line..' '..final[i] end print(line) line = ''
for j = 0,#initial do
if j > 0 then line = ' '..initial[j]
else line = ' .' end
for i = 0,#final do
local x = deltas[j][i]
line = line..(x and string.format('%3d',x) or ' . ')
end
print(line)
end
end
function LevenshteinDistance(initial,final)
local deltas = {}
for j = 0,#initial do deltas[j] = {} end
local i,j = 0,0
for i = 1, #final do deltas[0][i] = i end
for j = 1, #initial do deltas[j][0] = j end
deltas[0][0] = 0
for i = 1, #final do
for j = 1, #initial do
if initial[j] == final[i] then deltas[j][i] = deltas[j-1][i-1]
else
deltas[j][i] = math.min(deltas[j-1][i],deltas[j-1][i-1],deltas[j][i-1]) + 1
end
end
end
return deltas[#initial][#final],deltas
end
-- Get (an) optimal edit sequence from the delta table
function EditSequence(initial,final,deltas)
local subs = {}
local insdels = {}
local i,j = #final,#initial
local op, v -- next step to take
local NOP, SUB, INS ,DEL = 0,1,2,3 -- three possible steps to take
local v1,v2,v3 -- score of possible steps
while i > 0 and j > 0 do -- backtrack until origin
v1, v2, v3 = deltas[j-1][i-1] or math.huge, deltas[j][i-1] or math.huge, deltas[j-1][i] or math.huge
-- Find best route, prefering sub if best or equal score to others
op, v = SUB, v1
if v2 < v then op,v = INS, v2 end
if v3 < v then op,v = DEL, v3 end
-- use 'i' the position in final string, which assumes all preceeding ins/dels have been executed
if op == SUB then
if final[i] ~= initial[j] then
subs[#subs+1] = {final[i],i}
end
i,j = i-1,j-1
elseif op == INS then -- I swear this seems backwards
subs[#subs+1] = {final[i],i}
insdels[#insdels+1] = {'+',i}
i,j = i-1,j
else
insdels[#insdels+1] = {'#',i}
i,j = i,j-1
end
end
-- combine and reverse edits
local edits = {}
for i = 1,#insdels do edits[i] = insdels[#insdels - i + 1] end
for i = 1,#subs do edits[i+#insdels] = subs[#subs - i + 1] end
return edits
end
-- ============================== End Work Functions
-- ============================== Begin Read Functions
-- Read primary and secondary structure and populate dialog fields
function ReadStructure()
local n = structure.GetCount()
local tmp = {}
local first, last = ParseRangeString(dialogs.read.range.value)
local tmp2 = {}
tmp = {}
local rc, aa
for i = first,last do
if dialogs.read.onlym.value == false or structure.IsMutable(i) == true then
tmp[#tmp + 1] = structure.GetAminoAcid(i)
tmp2[#tmp2 + 1] = structure.GetSecondaryStructure(i)
else
tmp[#tmp + 1] = ' '
tmp2[#tmp2 + 1] = ' '
end
end
-- if (#tmp < last) then last = #tmp end
dialogs.read.range.value= string.format('%d - %d',first,last)
dialogs.read.primary.value = table.concat(tmp,'')
dialogs.read.sec1.value = table.concat(tmp2,'')
local dontprintlist = {M=true}
dontprintlist[' '] = true
if dialogs.read.onlym.value == false then dontprintlist['L'] = true end
dialogs.read.sec2.value = PrintCompressed(Compress(dialogs.read.sec1.value,first),dontprintlist)
print('Structure read from range:',first,'to',last)
print('Primary Structure (AA sequence):')
print(dialogs.read.primary.value)
print('Secondary Structure:')
print(dialogs.read.sec1.value)
print('Compressed Secondary Structure:')
print(dialogs.read.sec2.value)
end
-- ============================== End Read Functions
-- ============================== Begin Write Functions
function WritePrimaryStructure()
local n = structure.GetCount()
local tab
local input = string.lower(dialogs.write.primary.value)
local aalist = string.lower('ACDEFGHIKLMNPQRSTVWY +#')
local aa = {} -- valid aa lookup table
for i = 1,#aalist do aa[string.at(aalist,i)] = true end
tab = StringToTable(input,dialogs.write.range.value)
if tab == nil then return false end
-- Error check AA input
for i = 1,#tab do
local x = tab[i]
if x[2] < 1 or x[2] > n or x[3] < 1 or x[3] > n then
dialog.ShowMessageBox(
{'Your primary structure is out of range','(check start and length)'}
,'Input Error')
return false
end
if aa[x[1]] == nil then
dialog.ShowMessageBox(string.format(
'Unrecognized AA symbol: %s at %d',x[1],x[2]),'Input Error')
return false
end
if x[1] == '+' or x[1] == '#' then
dialog.ShowMessageBox(
{'Foldit has no way to insert or delete segments','in recipes. You must do it by hand.'},
'Sorry')
return false
end
end
-- Set AA
for i = 1,#tab do
local x = tab[i]
if x[1] ~=' ' then
if x[3] == nil or x[2] == x[3] then structure.SetAminoAcid(x[2],x[1])
else
selection.DeselectAll()
selection.SelectRange(x[2],x[3])
structure.SetAminoAcidSelected(x[1])
end
end
end
return true
end
function WriteSecondaryStructure()
local n = structure.GetCount()
local tab
local input = string.upper(dialogs.write.sec1.value)
local ss = { H='H', E='E', L='L', I='L', B='L', S='L', T='L', M=' ', G='H'}
ss[' '] = ' ' -- table of allowed SS, reduced to 'EHL '
tab = StringToTable(input,dialogs.write.range.value)
if tab == nil then return false end
-- Error check SS input
for i = 1,#tab do
local x = tab[i]
if x[2] < 1 or x[2] > n or x[3] < 1 or x[3] > n then
dialog.ShowMessageBox(
{'Your secondary structure is out of range','(check start and length)'}
,'Input Error')
return false
end
x[1] = ss[x[1]]
if x[1] == nil then
dialog.ShowMessageBox(string.format(
'Unrecognized AA symbol: %s at %d',x[1],x[2]),'Input Error')
return false
end
end
-- Set SS
if dialogs.write.setloops.value == true then
selection.SelectAll()
structure.SetSecondaryStructureSelected('L')
end
for i = 1,#tab do
local x = tab[i]
if x[1] ~= ' ' then -- no changes for ' '
if x[2] == x[3] then structure.SetSecondaryStructure(x[2],x[1])
else
selection.DeselectAll()
selection.SelectRange(x[2],x[3])
structure.SetSecondaryStructureSelected(x[1])
end
end
end
return true
end
-- Error trapping wrapper for setting PS and SS
function WriteStructure()
local rc
selection.DeselectAll()
rc = WritePrimaryStructure()
if rc == false then return false end
rc = WriteSecondaryStructure()
return rc
end
-- ============================== End Write Functions
-- ============================== Begin Compare Functions
-- Apply the PS edits to the SS so that length matches
function ApplyEditsToSS(editlist,sequence)
for i = 1,#editlist do
if editlist[i][1] == '+' then
table.insert(sequence,editlist[i][2],' ')
elseif editlist[i][1] == '#' then
table.remove(sequence,editlist[i][2])
end
end
end
function CompareSecondary(initial,final)
local diff = {}
for i = 1,#initial do
if initial[i] == ' ' or final[i] == initial[i] then
diff[i] = ' ' else diff[i] = final[i] end
end
local difflist1, difflist2
local dontprintlist = {M=true}
dontprintlist[' '] = true
difflist1 = table.concat(diff,'')
difflist2 = PrintCompressed(Compress(difflist1,1),dontprintlist)
print('SS differences:')
print(difflist1)
print('Compressed Secondary Structure:')
print(difflist2)
dialogs.diff.secondary.value = difflist1
dialogs.diff.sscompressed.value = difflist2
end
function ComparePrimary(initial,final)
local edit_distance, edit_table = LevenshteinDistance(initial,final)
print('Edit distance:',edit_distance)
edit_sequence = EditSequence(initial,final,edit_table)
local psdiff = PrintCompressed(edit_sequence)
print('PS differences:')
print(psdiff)
dialogs.diff.primary.value = psdiff
return edit_sequence
end
n_starts = 0 -- computed on first auto-loop
resetscore = nil
startcounter = 0
-- Work out what to compare against, load it, then do comparisons
function CompareStructure(current_ss,current_ps)
local function GetNextStart()
puzzle.StartOver()
print('Resetting...score =',current.GetScore())
if resetscore == nil then resetscore = current.GetScore()
elseif n_starts == 0 and resetscore == current.GetScore() then
n_starts = startcounter
print('Determined the number of starts to be',n_starts)
end
startcounter = startcounter + 1
src = 'Next Reset'
end
local function GetThisStart()
print('Looping through all starts to get back to current start...')
if (n_starts == 0) then print('First need to determine the number of starts...') end
local i = 1
GetNextStart()
while n_starts == 0 do
GetNextStart()
i = i + 1
end
while i % n_starts > 0 do
GetNextStart()
i = i + 1
end
src = 'This Reset'
end
src = ''
local function GetSS9()
print('Loading SS from Saved Structure')
src = 'Saved Structure'
save.LoadSecondaryStructure()
return true
end
local function GetSSST()
dialog.ShowMessageBox('ST Save SS not implemented','Sorry')
src = 'ST Save SS'
return false
end
local function GetUser()
print('Loading PS & SS from save #',dialogs.change.slot.value)
src = 'Quicksave Slot #'..tostring(dialogs.change.slot.value)
save.Quickload(dialogs.change.slot.value)
return true
end
local options = { 'next', 'this', 'ss9', 'ssst', 'user' } -- checkboxes
local functions = { GetNextStart, GetThisStart, GetSS9, GetSSST, GetUser }
local selection = 0
local n_selected = 0
for i,v in ipairs(options) do
if dialogs.change[v] ~= nil then -- in case I delete a box
if dialogs.change[v].value == true then
selection = i
n_selected = n_selected + 1
end
end
end
if n_selected ~= 1 then
dialog.ShowMessageBox('Pick one save to compare against.','Input Error')
return
end
local rc = functions[selection]() -- Load the desired comparison structure
if rc == false then
print('Unable to load comparison structures')
return
end
first, last = 1,structure.GetCount()
local initial_ss, initial_ps = {},{}
for i = first,last do
initial_ss[i] = structure.GetSecondaryStructure(i)
initial_ps[i] = structure.GetAminoAcid(i)
end
if dialogs.change.reverse.value then
dialogs.diff.label.label = 'Computing changes from Current to '..src
current_ss,initial_ss = initial_ss,current_ss
current_ps,initial_ps = initial_ps,current_ps
else
dialogs.diff.label.label = 'Computing changes from '..src..' to Current'
end
print(dialogs.diff.label.label)
local editlist = {}
editlist = ComparePrimary(initial_ps,current_ps)
ApplyEditsToSS(editlist,initial_ss)
if (#initial_ss ~= #current_ss) then
print(table.concat(initial_ss))
print(table.concat(current_ss))
error('Initial and current SS aren\'t the same length after applying edits!')
end
CompareSecondary(initial_ss,current_ss)
dialog.Show(dialogs.diff)
end
-- ============================== End Compare Functions
-- ===================== Begin Dialog Logic
-- Dialog to test the compress/decompress functions
function Test()
local d = dialog.CreateDialog('Test')
d.input = dialog.AddTextbox('Input','')
d.out1 = dialog.AddTextbox('Uncomp.','')
d.out2 = dialog.AddTextbox('Comp.','')
d.b1 = dialog.AddButton('Test',1)
d.b2 = dialog.AddButton('Exit',0)
while true do
local rc = dialog.Show(d)
if rc == 0 then return end
local tab = StringToTable(d.input.value)
if tab ~= nil then
d.out2.value = PrintCompressed(tab)
d.out1.value = Uncompress(tab)
end
end
end
function DoDetectMenu()
local d = dialogs.change
-- save the structure for later in case we use reset
save.Quicksave(qsave_err)
-- get the current ss now in case we reset
local current_ss, current_ps = {}, {}
for i = 1,structure.GetCount() do
current_ss[i] = structure.GetSecondaryStructure(i)
current_ps[i] = structure.GetAminoAcid(i)
end
repeat
local rc = dialog.Show(d)
if rc == d.compare.value then CompareStructure(current_ss,current_ps) end
until rc == d.back.value
save.Quickload(qsave_err)
end
function DoReadMenu()
local d = dialogs.read
repeat
local rc = dialog.Show(d)
if rc == d.read.value then ReadStructure()
elseif rc == d.detect.value then DoDetectMenu()
end
until rc == d.back.value
end
function DoWriteMenu()
local d = dialogs.write
repeat
local rc = dialog.Show(d)
if rc == d.write.value then
save.Quicksave(qsave_err)
local rc1,rc2 = pcall(WriteStructure)
if rc1 == false then print('Error message:',rc2) end
if rc1 == false or rc2 == false then
print('Undoing attempted changes.')
save.Quickload(qsave_err)
end
selection.DeselectAll()
elseif d.undo and rc == d.undo.value then
save.Quickload(qsave_slot)
print('All changes to primary and secondary structure undone.')
dialog.ShowMessageBox('All changes to primary and sec. structure undone!')
elseif rc == d.clear.value then
d.primary.value = ''
d.sec1.value = ''
d.range.value = ''
end
until rc == d.back.value
end
function ShowAbout()
local i = 1
repeat
local rc = dialog.Show(dialogs.about[i]) -- +- 1 or 0
i = i + rc
until rc == 0
end
function DoMainMenu()
local d = dialogs.main
repeat
local rc = dialog.Show(d)
if rc ~= d.exit.value and qsave_slot == nil then SetQuicksave() end
if rc == d.help.value then ShowAbout()
-- Read Submenu
elseif rc == d.read.value then DoReadMenu()
-- Write Submenu
elseif rc == d.write.value then DoWriteMenu()
end
until rc == d.exit.value
end
-- ===================== End Dialog Logic
-- ===================== Begin Main
--Test()
if beta_date and beta_date < os.time() then
dialog.ShowMessageBox({'This beta software has expired.','Please download a new copy today!'},'Recipe Update')
end
if structure.HasMolecule() then
print('Note: structure has a molecule, which will be ignored.')
end
print(puzzle.GetName(),os.date(),ui.GetTrackName(),current.GetScore())
CreateDialogs()
DoMainMenu()