ラベル C# の投稿を表示しています。 すべての投稿を表示
ラベル C# の投稿を表示しています。 すべての投稿を表示

2010年12月16日木曜日

R.NETというのが出てた。

http://rdotnet.codeplex.com/

これなら.NETからもデバッグできるし、開発がラクチンになるわ。

ただ.NET4対応ということで3.5に対応していないこと。
どうしてVS2010でつくるかなー。せめて3.5にしてくれよ。

この本も欲しいな。



2010年4月6日火曜日

Listオブジェクトから特定のIndex(複数)をRemove

Listオブジェクトを使っていると、特定のIndexに相当する値を除去したくなる時があります。

単一のIndexならRemoveメソッドを使えば取り除けますが、複数の項目を同時にRemoveしようとするとうまくいきません。これはひとつの項目を除去するとListのIndexが更新されるので、次のIndexに相当する項目が変わってしまうからです。

Delegateを使ってRemoveAllメソッドを使えばできるようですが、私はこのDelegateがどうも苦手でダメなので、代わりに除去したListを返す関数を作ることで対応してます。

これってたぶん亜流なんでしょうね。



using System.Collections.Generic;
using System.Diagnostics;


namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            // Create Start List
            List<int> Ints = new List<int>();
            for (int i = 0; i < 100; i += 10)
            {
                Ints.Add(i);
            }
            
            //Create Delete Index List
            List<int> Deletes = new List<int>();
            Deletes.Add(2);
            Deletes.Add(4);


            // Do Delete
            List<int> result = DeleteFromIndex(Ints, Deletes);


            // Show Result
            foreach (int value in Ints)
            {
                Debug.WriteLine(value.ToString());
            }
            foreach (int value in result)
            {
                Debug.WriteLine(value.ToString());
            }


        }
        /// <summary>
        /// Delete values from index
        /// </summary>
        /// <param name="inList">Original List</param>
        /// <param name="inDeleteIndex">Index list for deletion</param>
        /// <returns>Deleted list</returns>
        private static List<int> DeleteFromIndex(List<int> inList,List<int> inDeleteIndex)
        {
            List<int> rt = new List<int>();


            for (int i = 0; i < inList.Count; i++)
            {
                if (!inDeleteIndex.Contains(i))
                {
                    rt.Add(inList[i]);
                }
            }
            return rt;
        }
    }
}

2010年2月13日土曜日

IUPAC名からSmilesを起こす

ということで苦労しまくったIUPAC名からSmilesを起こすプロジェクトですが、最終的には以下のような流れになりました。

1)Opsin0.5.3でIUPAC名をCMLに変換
2)CDKでCMLをIMoleculeに変換
3)CDKでIMoleculeからMolファイル形式に変換
4)OpenBabelでMolファイルからSmilesに変換

フォームアプリケーション的はコードを書きます。Smilesにするだけなら2次元構造は必要なないのですが、忘記録的に入れときます。この流れがdelegateでも動いたらOkですな。




using System;
using System.Windows.Forms;
using System.IO;
using System.Diagnostics;


using org.openscience.cdk.io;
using org.openscience.cdk;
using org.openscience.cdk.interfaces;
using org.openscience.cdk.layout;
using java.io;


using uk.ac.cam.ch.wwmm.opsin;
using OpenBabel;



namespace OpsinTest
{
    public partial class Form1 : Form
    {
        NameToStructure nts = null;



        public Form1()
        {
            InitializeComponent();
            nts = new NameToStructure();
        }



        private void button3_Click(object sender, EventArgs e)
        {
            string IUPAC = textBox1.Text;


            try
            {
                textBox2.Text = "";
                Application.DoEvents();


                // Create CML from IUPAC name (Opsin)
                string cml = nts.parseToCML(IUPAC).toXML();
                
                // Convert CML to IMolecule (CDK)
                StringBufferInputStream str_stream  = new StringBufferInputStream(cml);
                
                CMLReader cmlr = new CMLReader();
                cmlr.setReader(str_stream);


                ChemFile chemFile = new ChemFile();
                ChemFile chem = (ChemFile)cmlr.read(chemFile);


                IMolecule mol = chem.getChemSequence(0).getChemModel(0).getMoleculeSet().getMolecule(0);


                // Convert from IMolecule to SD (CDK)
                java.io.StringWriter sww = new java.io.StringWriter();
                MDLWriter mw = new MDLWriter(sww);
                StructureDiagramGenerator sdg = new StructureDiagramGenerator();


                sdg.setMolecule(mol);
                sdg.generateCoordinates();
                mol = sdg.getMolecule();
                mw.write(mol);
                string sd = sww.toString();


                // Convert from SD to smiles (OpenBabel)
                OBMol obMol = new OBMol();
                OBConversion obConv = new OBConversion();
                if (!obConv.SetInAndOutFormats("mol", "can")) { return; }
                if (!obConv.ReadString(obMol, sd)) { return; }


                textBox2.Text = obConv.WriteString(obMol);


                obMol.Dispose();
                obConv.Dispose();
                obMol = null;
                obConv = null;
            }
            catch (Exception ex)
            {
                string mes = ex.Message;
                textBox2.Text = mes;


            }
        }

    }
}

OBConversionでメモリーリーク?

OpenBabelの構造コンバーターの「OBConversion」はめちゃくちゃたくさんの構造フォーマットに対応した便利なConverterなんですが、どうも変な動きをすることがあります。

たとば、下記のようにMolの配列にSmiles文字列の配列をConvertしながら入れる場合、下記のコードはVisualStudioでは特に問題ありません。

(コードA)
for(i=0; i < 3; i++)
{
    OBConversion conv = new OBConversion();
    conv.SetInAndOutFormats("smi", "can");
    conv.ReadString(obMol[i], obSmi[i]);
}

しかし下記のコードのように、CML文字列を変換する場合、実行中は何も問題ないのですが、終了するとメモリーエラーを起こします。

(コードB)

for(i=0; i < 3; i++)
{
    OBConversion conv = new OBConversion();
    conv.SetInAndOutFormats("cml", "can");
    conv.ReadString(obMol[i], obCml[i]);
}

これは正しくは、こうするべきです。

(コードC)
for(i=0; i < 3; i++)
{
    OBConversion conv = new OBConversion();
    conv.SetInAndOutFormats("cml", "can");
    conv.ReadString(obMol[i], obCml[i]);

    conv.Dispose();
    conv = null;
}

つまり、使用後のOBConversionインスタンスはちゃんと破棄する必要があります。このコードでは終了しても何もエラーがなく正常に動きます。

ただ不思議なのはコードAではメモリーエラーを起こさず、コードBではエラーを起こす点です。

想像するにCMLのConvertで何か内部的なメモリーの破棄忘れがあって、破棄しない場合にメモリーリークを起こしているではと思ってます。

さらにはコードCでもメモリーエラーを起こす場合があります。これはコードCを含む関数をdelegateで使う場合で、OBConversionのインスタンスが正常に破棄されず、ReadStringの箇所でメモリーエラーを起こします。

ということで、実用問題としてOpenBabelのCML変換は使えない、という結論に達しました。

これがわかるのに2日もかかりました。

ということでCML変換はCDKでやることにしました。その内容はまた別に書きます。

OpsinをC#で使う

以前に.NETでCDKを使うで書いたように、JAVAの実行ファイル(.jarファイル)を.NETのclass library(dllファイル)にConvertする方法を使えば、いろいろなJavaの便利環境を.NETで使うことができます。

今回は私の心の師匠のkさんのhttp://blog.kzfmix.com/entry/1214827204で使っていたOpsinを.NETで使えるか試してみました。

Opsinは化合物のIUPAC名をCMLに変換するモジュールで、CMLからさらにSDやSmilesなどに変換することができます。これを使えば、論文や特許に記載されている化合物の文字列から構造を起こすことができるという優れものです。

使ったOpsinはOscar3に入っているOpsin-0.5.3.jar。実は最初に0.1.0を試したのですが、どうもIUPACの認識率が低く、使い物にならんなーと思って諦めかけたのですが、このバージョンになってだいぶ良くなったので使うことにしました。

いつもどおりにIKVMでjarをdllに変換します。このときkeyfileがあれば指定することもできます。keyfile付きで変換することで「厳格な名前」付きdllにすることができるというわけです。

出来上がったdllをVisualStudioのC#のプロジェクトにいれます。そして

using OpenBabel;
using uk.ac.cam.ch.wwmm.opsin;


NameToStructure nts = new NameToStructure();
string cml = nts.parseToCML("4-iodobenzoic acid").toXML();


OBMol obMol = new OBMol();
OBConversion conv = new OBConversion();
conv.SetInAndOutFormats("cml", "can");
conv.ReadString(obMol, cml);
string smiles = conv.WriteString(obMol);
string[] smile = smiles.Split(new string[] { "\t" }, StringSplitOptions.None);
string result = smile[0];

こんな流れのコードを使えばIUPAC名からSmiles文字列を作ることができます。

ただ、特許や論文のIUPAC名はけっこういい加減で、" ' - などの使い方、あるいは半角スペースの入り方でOpsinがエラーを吐くことが多いです。Opsinに渡す前にIUPAC名のCleanが必要なようです。

またここで書いたOBConversionクラスですが、どうもメモリーリークがありそうなので、CMLの変換には使わない方が良いでしょう。このあたりについては次に・・・・

2010年2月4日木曜日

Spotfire DecisionSiteで.NET

ずっとTIBCO Spotfire+C#で仕事していると、たまにSpotfire DecisionSiteのスクリプトをいじらなきゃいけなくなったときに超めんどくさくなります。

なんといってもデバッグできない。昔はしこしこJavascriptでAlertとかログファイルに書き出したりしてなんとか構築したのに、もう戻れなくなっている。

ということで、DecisionSiteでVisualStudio C#を使う方法を検討しました。もちろん直接Javascriptのデバッグもできるとは思いますが、それじゃ面白くないので、C#でクラスライブラリーを作ってそれを参照させることにしました。

VisualStudio C#でクラスライブラリーを作ります。このとき「COM参照」ができるようにPropertiesを設定しておきます。

ClassはPublicで作って普通にコンパイルするとdllとtblファイルが出来ます。この2つのファイルをDecisionSiteのガイドやツールに登録してやればJavascriptのnew ActiveX(<namespace.class名>)で使えるようになります。あとは登録したpublic methodを呼べば使えるというわけです。

思っていたより結構気軽なので、よく使うレコードセット処理や文字列処理を入れて使う予定です。

2010年1月28日木曜日

SOM

TIBCO SpotfireでSOMをすべく、R Integrationを作ってます。

とりあえず版はできたけど、精度が大丈夫なのかがよく分からない。DSの結果と比べて検証しなきゃ。

2010年1月26日火曜日

OpenBabelでOBMolインスタンスのコピー

OpenBabelでOBMolのインスタンスをコピーする時、デバッグに苦労する現象に遭遇しました。

OBMol mol1 = new OBMol();
OBMol mol2 = new OBMol();
(この間、mo1,2lの操作をする)
mol1 = mol2;

この代入でエラーは出ませんが、mol1のインスタンスは非常に不安定で、突然消えたりします。

これは、mol1のインスタンスのメモリー領域がmol2で上書きされることで、(おそらく)mol1のポインターが確保されないまま操作されていることによります。たちの悪いことに、この操作でのエラーは何も出ません。

mol1にmol2を上書きする場合は

mol1 = new OBMol(mol2)

として、mol1をちゃんと初期化してmol2をコピーしましょう。

これが原因で10日ほど悩みました。

2010年1月23日土曜日

構造編集用の.NET Framework Class library:Hyleos

構造編集に便利そうな.NET Framework Class libraryを見つけました。

http://www.hyleos.net/

このサイトが会社なのか有志なのか分かりません。少なくともOpen Sourceではなさそうです。でもFreeでの配布とのことでちょっと使ってみました。

ここで配布しているClass libraryを使うと、構造編集用のBoxが使えて、ダブルクリックでDrawingソフトが起動し、構造の修正ができます。

ISISのStructure Boxのような感じで、とりあえずSymyx Drawとの連動は正常に動作しました。

構造編集ソフトとの連携が通常のようにMIMEタイプでやり取りしていればほとんどのDraw系ソフトと連携できるはずで、ISIS Drawとの連携も出来そう。

Boxからの構造の取り出し、Smiles変換機能など、必要な機能は一通り揃っています。OpenBabelへのSmilesでの連携も確認できました。

早速、月曜日に試してみようっと。ちゃんと動作すればTIBCO Spotfireへ組み込んでみよう。

2010年1月19日火曜日

TIBCO Spotfire上でsdf2smi

っつーわけでCDKをTIBCO Spotfireに組み込んでsdf2smi、Daylightで言うところのmol2smiを作ってみました。

とりあえず動きました。とりあえずというのは遅い!もう速くするのが課題。

ついでに言うと、OpenBabelを使ったsmi2sdf、Daylightでいうところのsmi2molも組み込みました。こちらはそこそこ実用的な速度が出てます。

このあたり、JavaのLibraryを少し無理に.NET Frameworkに移した影響があるのかも。
ようはOpenBabelが早く2D変換をサポートしてくれれば済むのですが。

2010年1月17日日曜日

JMEも.NETで使える

JMEも.NETで使えるらしい。

http://chemhack.com/archives/tag/chemoinformatics/

.NETでCDKを使う

CDKはjavaのmolecular class libraryのようなものです。java版のpipeline pilotであるKnimeではメインのChemistry engineとして採用されています。

CDKを.NET環境で使うためのやり方が紹介されていたのでやってみました。

Using the CDK with the .Net framework and Mono

まずjavaのjarファイルを.NETのクラスライブラリー化するためのツールIKVMをダウンロードします。私が落としたのは「ikvmbin-0.42.0.3」です。

ついでCDKのjarファイルを落とします。私が落としたのは「cdk-1.2.4.1.jar」です。

IKVMを使ってこのCDKをdll化します。
コマンドプロンプトでikvm.exeのあるフォルダーに移動して

ikvmc -assembly:cdk_dotnet -target:library cdk-1.2.4.1.jar

と打つとcdk_dotnet.dllが出来ます。この際にWarningが多く出ますが無視。

VisualStudio2008のC#のコンソールアプリケーションを作成して、参照設定にcdk_dotnet.dllを含めikvmのbinフォルダーの中のdllをすべて登録します。



これでCDKが使えるようになりました。

具体的なコードは上記のサンプルコードを参照してください。Molファイルを読み込んで計算値を出力してくれました。

using System;

namespace CDK_test
{
    //Using aliases for convenience and to avoid importing whole
    //packages
    using FReader = java.io.FileReader;
    using TPSA = org.openscience.cdk.qsar.descriptors.molecular.TPSADescriptor;
    using LogP = org.openscience.cdk.qsar.descriptors.molecular.XLogPDescriptor;
    using DoubleResult = org.openscience.cdk.qsar.result.DoubleResult;
    using Builder = org.openscience.cdk.DefaultChemObjectBuilder;
    using IMol = org.openscience.cdk.interfaces.IMolecule;
    using MolReader = org.openscience.cdk.io.iterator.IteratingMDLReader;
    using Consts = org.openscience.cdk.CDKConstants;

    class Program
    {
        static void Main(string[] args)
        {
            FReader fReader = new FReader("C:\Test.mol");
            MolReader molReader = new MolReader(fReader, Builder.getInstance());
            IMol mol;
            DoubleResult dr;
            LogP logP = new LogP();
            TPSA tpsa = new TPSA();
            double logPVal, tpsaVal;
            string name;

            while (molReader.hasNext())
            {
                mol = (IMol)molReader.next();
                dr = (DoubleResult)logP.calculate(mol).getValue();
                logPVal = dr.doubleValue();
                dr = (DoubleResult)tpsa.calculate(mol).getValue();
                tpsaVal = dr.doubleValue();

                //the title of each mol in the file is the name of the mol
                name = (String)mol.getProperty(Consts.TITLE);

                Console.WriteLine("{0} {1} {2}", name, logPVal, tpsaVal);
            }
            Console.ReadLine();
        }
    }
}

今回は純粋な.NETのclass libraryしか参照していないので、そのままSpotfireのツールに出来そうです。

2009年12月17日木曜日

OpenBabel:SuperCanonicalizer()

前回は価数の超過に対応して、もうこれで大丈夫と思ったのですが、またまた落とし穴を発見。

今回はStereo Isomerです。

Smilesでは/はUpper Bond、\はDown Bondを表します。これらもinchiで除けるかと思ったのですがだめでした。inchiをかえしてもUpper/Downはそのままでした。

これは一発のコマンドではどうしようもなかったので

foreach (OBBond oneBond in Mol.Bonds())
{
    if(OneBond.IsUp()){oneBond.UnsetUp();}
    if(OneBond.IsDown()){oneBond.UnsetDown();}
}

というような感じで、一個一個Bondをチェックして標準化しました。

しかし、SmilesのくせにStereo Isomerを表現するとは生意気だ。

まだ落とし穴があるかも・・・。

2009年12月16日水曜日

OpenBabel:Tautomer(互変異性)を標準化する

前回、Tautomer(互変異性)を標準化する関数を作ったのですが、落とし穴がありました。

よくあることなのですが、自動で部分構造をSmilesで切り出す際に、原子の価数が超過してしまうことがあります。たとえばこのような構造が出力されることがあります。



この分子の原子のうちSは価数を超えているのでS+と表記すべきですが、Smilesではそのような表現がされないまま出力されてしまいます。これを先日のTautomersStandardizerにかけても修正されません。修正されないどころか、場合によっては芳香性もなくなって変な脂溶性構造になることまであります。こわい。

しかし、変換前に水素を全部取ってやるとちゃんとした




になることがわかりました。

ついでに関数名も変えて、Canonicalで出力するようにしてます。



static string SuperCanonicalizer(string inSmiles)
{
OBMol mol = new OBMol();
OBConversion obconvSmiToInchi = new OBConversion();
OBConversion obconvInchiToSmi = new OBConversion();

obconvSmiToInchi.SetInFormat("smi");
obconvSmiToInchi.SetOutFormat("inchi");
obconvInchiToSmi.SetInFormat("inchi");
obconvInchiToSmi.SetOutFormat("can");

obconvSmiToInchi.ReadString(mol, inSmiles);
mol.DeleteHydrogens();
obconvInchiToSmi.ReadString(mol,obconvSmiToInchi.WriteString(mol));


return obconvInchiToSmi.WriteString(mol);
}



これでどんなSmilesもすべて標準化できそうです。

2009年12月10日木曜日

OpenBabel:Tautomer(互変異性)を標準化する

芳香環を持つ化合物は共鳴構造を取ることから、表記上複数の構造を取りえます。

しかし、実際はひとつの同じ化合物であり、コンピューター上で構造の重複処理をする際に問題となります。このような、表記上異なる構造であるが同一の構造であることをTautomer(互変異性)といいます。

Smilesで処理するとどうしてもこのTautomer(互変異性)を標準化・同一化することが出来ません。お金を出して専用ソフトを買うとたいていこのTautomer処理機能はついています。

しかしOpenBabelでもその機能を組み合わせるとこのTautomer(互変異性)標準化ができます。

OpenBabelはさまざまな化合物形式をサポートしていますので、これを使ってやってみます。使うのがInChIという構造フォーマットです。

InChIは、立体構造を含めた化合物の構造を一意的に記述することを目的にIUPACと米国の国立標準技術研究所(NIST)が策定した標準。また、機械処理のための短い識別子であるInChIKeyも各化合物に付与される。既に普及している米国化学会のCAS登録番号と異なり、誰でも自由に使えることが特徴。・・・とのことです。

SmilesをいったんInChIに変換するとTautomer(互変異性)は同一の記述になります。そこでそのあとでまたSmilesに戻してやると同じSmilesになるというわけです。

using System;
using OpenBabel;

namespace SmilesTest
{
class Program
{
static OBConversion obconvSmiToInchi = new OBConversion();
static OBConversion obconvInchiToSmi = new OBConversion();

static void Main(string[] args)
{
OBMol tautomer1 = new OBMol();
OBMol tautomer2 = new OBMol();

string smiles1 = "Oc1ccccn1";
string smiles2 = "O=c1cccc[nH]1";

Console.WriteLine(smiles1);
Console.WriteLine(smiles2);

Console.WriteLine(TautomersStandardizer(smiles1));
Console.WriteLine(TautomersStandardizer(smiles2));

Console.In.ReadLine();
}
/// <summary>
/// Tautomers Standardizer
/// 互変異性標準化する関数
/// tautomerの構造の違いをStandarlizeする
/// </summary>
/// <param name="inSmiles">Smiles文字列</param>
/// <returns>標準化Smiles文字列</returns>
static string TautomersStandardizer(string inSmiles)
{
OBMol mol = new OBMol();
OBConversion obconvSmiToInchi = new OBConversion();
OBConversion obconvInchiToSmi = new OBConversion();

obconvSmiToInchi.SetInFormat("smi");
obconvSmiToInchi.SetOutFormat("inchi");
obconvInchiToSmi.SetInFormat("inchi");
obconvInchiToSmi.SetOutFormat("smi");

obconvSmiToInchi.ReadString(mol, inSmiles);
obconvInchiToSmi.ReadString(mol,obconvSmiToInchi.WriteString(mol));


return obconvInchiToSmi.WriteString(mol);
}
}
}

これを実行すると下記のようになります。



やっぱOpenBabeは便利やわ。



2009年12月8日火曜日

OpenBabel:OBMol.Bonds()とDeleteBond()の関係

OpenBabelの分子を表現するClassにOBMolがあります。OpenBabelの最も重要なClassです。

化合物の構造を変換するときなどはOBMolに含まれるMethodをいろいろ使うわけですが、その中のBonds()というMethodがあります。このMethodはMolに含まれるBondすべてのIEnumerableリストを返します。

例えば、その中の特定のBondを切断したければ

foreach(IEnumerable<OBBond> oneBond in inMol.Bonds())
{
if(・・・条件式・・・)
{inMol.DeleteBond(oneBond);}
}

などとやりますが、これでは実は正常に動きません。

DeleteBond()を呼ぶとBonds()で取得できるIEnumerable<OBBond>の中身がどうも変わってしまうようで、一度に複数回DeleteBondをCallすると予期せぬBondが切れてしまいます。

よって、DeleteBondを呼んだあとで、今一度
IEnumerable<OBBond> oneBond in inMol.Bonds()
などでリストを再構築する必要があります。

この原因がわかるのに1日を費やしました(涙)。

2009年12月4日金曜日

OpenBabel:Aromaticity

分子がAromaticかどうか知りたいときがありますよね。
そんなときのお気楽関数「Aromaticity」です。

原理は単純。Aromatic原子数/全原子数だけです。
完全Aromatic分子なら1、Aliphaticなら0になるというわけです。

using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Diagnostics;

using OpenBabel;

・・・・・・・

/// <summary>
/// Aromaticity:Aromatic原子数/全原子数
/// </summary>
/// <param name="inMol">OBMolインスタンス</param>
/// <returns>0~1の数値</returns>
public static double Aromaticity(OBMol inMol)
{
int atomCount = 0;
int aromaticAtomCount = 0;
foreach (OBAtom item in inMol.Atoms())
{
if (item.IsAromatic()) { aromaticAtomCount += 1; }
atomCount += 1;
}
return aromaticAtomCount / atomCount;
}

2009年12月3日木曜日

OpenBabel : AtomのIndexリストからOBMolオブジェクトを再構築する

OpenBabelでSMARTSを使ったSmilesの部分構造検索をすると、結果がMapListと呼ばれる原子のIndexのリストで帰ってきます。OpenBabelではこのIndexを使うことを推奨していません。なぜならこのIndexは分子をModify(一部構造を切り取る、原子・分子を付加するなど)するとIndexが再作成されてしまうので、永続的に保持されないからです。


といってもこの原子Indexは元の分子をいじらない限りは変更されないので、元の分子の一部分を記憶させるには使いやすいです。検索結果や部分構造を別途Indexとして持っていればいろいろ使えます。ようは元の構造を一切いじらなければOKです。


しかも、これはやってみたのですが、DeleteBondするだけではIndexは変更されずちゃんと保持されます。つまり切断Bondとしての情報をもったままの分子として原子Indexがそのまま保持されます。Separateメソッドで元の分子をばらばらにしてしまうと、各部分構造分子の原子Indexは再作成されて、1から順番に振られます。


で、元の分子をいじらずに、取得した原子Indexから部分構造を再構築したいことはよくあります。なんでお気軽関数を作りました。コードはC#です。


新しいOBMolインスタンスに元の分子から原子をAddしていくのですが、Addしてしまうと新しい分子上のAddした原子のIndexはやはり1から順番に振られてしますので、別途新しいIndexと元のIndexの対応表をHashtableで管理しています。この情報を使ってあとでOBBondをAddしていきます。







using System.Collections.Generic;
using System.Collections;


using OpenBabel;

namespace OpenHabel
{
    static class OHUtil
    {
        /// <summary>
        /// ParentのMolと原子のIndexからMolを再構築
        /// </summary>
        /// <param name="parentMol"></param>
        /// <param name="atomIndex"></param>
        /// <returns></returns>
        public static OBMol GetMol(OBMol parentMol, List<uint> atomIndex)
        {
            Hashtable atomIdxTable = new Hashtable();
            OBMol newMol = new OBMol();
            IEnumerable<OBAtom> atoms = parentMol.Atoms();

            int atomCount = 0;
            foreach (OBAtom oneAtom in atoms)
            {
                if (OHUtil.IsThereItemInList(atomIndex, oneAtom.GetIdx()))
                {
                    OBAtom newAtom = new OBAtom();
                    newAtom.Duplicate(oneAtom);
                    int idx = int.Parse(oneAtom.GetIdx().ToString());
                    atomCount += 1;
                    atomIdxTable[idx.ToString()] = atomCount;

                    newMol.AddAtom(newAtom);

                    //Console.Write(obconv.WriteString(newMol));
                }
            }
            IEnumerable<OBBond> bonds = parentMol.Bonds();
            foreach (OBBond oneBond in bonds)
            {
                if (
                 OHUtil.IsThereItemInList(atomIndex, oneBond.GetBeginAtomIdx()) &&
                 OHUtil.IsThereItemInList(atomIndex, oneBond.GetEndAtomIdx())
                )
                {
                    int startAtomKey = (int)oneBond.GetBeginAtomIdx();
                    int startAtomIdx = (int)atomIdxTable[startAtomKey.ToString()];

                    int endAtomKey = (int)oneBond.GetEndAtomIdx();
                    int endAtomIdx = (int)atomIdxTable[endAtomKey.ToString()];

                    int order = (int)oneBond.GetBondOrder();

                    newMol.AddBond(startAtomIdx, endAtomIdx, order);
                }
            }
            return newMol;
        }

        /// <summary>
        /// atomList中に特定のatomIndexが含まれるかどうか
        /// </summary>
        /// <param name="atomList">List:uint</param>
        /// <param name="atomIndex">uint</param>
        /// <returns>見つかったらTrue</returns>
        public static bool IsThereItemInList(List<uint> atomList, uint atomIndex)
        {
            foreach (uint item in atomList)
            {
                if (item == atomIndex) { return true; }
            }
            return false;
        }
    }
}