[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: mark-watson\ncustom: [“https://buy.stripe.com/aFa5kudvI3mTfv5c5F8N200” stripe.com]\n"
  },
  {
    "path": ".gitignore",
    "content": "*~\n.DS_Store\nvenv\n.idea\n*.pyc\ntarget\nclient_secrets.json\ncl*.json\nindex.json\n*.log\ncache\n"
  },
  {
    "path": "CHAPTERS_and_CODE_no_longer_in-book/README.md",
    "content": "# Examples for older 2023 version of my book\n\nLook in sub-diurecotries for original book content that has been deprecated. The old book chapter is now a README.md file. For example, I deprecated the Zapier example so that book chapter is now available at [https://github.com/mark-watson/langchain-book-examples/blob/main/CHAPTERS_and_CODE_no_longer_in-book/zapier_integrations/README.md](https://github.com/mark-watson/langchain-book-examples/blob/main/CHAPTERS_and_CODE_no_longer_in-book/zapier_integrations/README.md) and the original Python code examples are in the same directory as the book chapter.\n\n"
  },
  {
    "path": "CHAPTERS_and_CODE_no_longer_in-book/zapier_integrations/README.md",
    "content": "# Using Zapier Integrations With GMail and Google Calendar\n\nZapier is a service for writing integrations with hundreds of cloud services. Here we will write some demos for writing automatic integrations with Gmail and Google Calendar.\n\nUsing the Zapier service is simple. You need to register the services you want to interact with on the Zapier developer web site and then you can express how you want to interact with services using natural language prompts.\n\n## Set Up Development Environment\n\nYou will need a developer key for [Zapier Natural Language Actions API](https://nla.zapier.com/get-started/). Go to this linked web page and look for \"Dev App\" in the \"Provider Name\" column. If a key does not exist, you'll need to set up an action to create a key. Click \"Set up Actions\" and follow the instructions. Your key will be in the Personal API Key column for the \"Dev App.\" Click to reveal and copy your key. You can [read the documentation](https://nla.zapier.com/api/v1/dynamic/docs).\n\nWhen I set up my Zapier account I set up three Zapier Natural Language Actions:\n\n- Gmail: Find Email\n- Gmail: Send Email\n- Google Calendar: Find Event\n\nIf you do the same then you will see the Zapier registered actions:\n\n![](zapier1.png)\n\n\n## Sending a Test GMail\n\nIn the following example replace **TEST_EMAIL_ADDRESS** with an email address that you can use for testing.\n\n```python\nfrom langchain.llms import OpenAI\nfrom langchain.agents import initialize_agent\nfrom langchain.agents.agent_toolkits import ZapierToolkit\nfrom langchain.utilities.zapier import ZapierNLAWrapper\n\nllm = OpenAI(temperature=0)\nzapier = ZapierNLAWrapper()\ntoolkit = ZapierToolkit.from_zapier_nla_wrapper(zapier)\nagent = initialize_agent(toolkit.get_tools(), llm, agent=\"zero-shot-react-description\", verbose=True)\n\nagent.run(\"Send an Email to TEST_EMAIL_ADDRESS via gmail that is a pitch for hiring Mark Watson as a consultant for deep learning and large language models\")\n```\n\nHere is the sample output:\n\n```console\n$ python send_gmail.py\n\n\n> Entering new AgentExecutor chain...\n I need to use the Gmail: Send Email tool\nAction: Gmail: Send Email\nAction Input: Send an email to TEST_EMAIL_ADDRESS with the subject \"Pitch for Hiring Mark Watson as a Consultant for Deep Learning and Large Language Models\" and the body \"Dear Mark Watson, I am writing to you to pitch the idea of hiring you as a consultant for deep learning and large language models. I believe you have the expertise and experience to help us achieve our goals. Please let me know if you are interested in discussing further. Thank you for your time.\"\nCc: not enough information provided in the instruction, missing Cc\nObservation: {\"labelIds\": \"SENT\"}\nThought: I now know the final answer\nFinal Answer: An email has been sent to TEST_EMAIL_ADDRESS with the subject \"Pitch for Hiring Mark Watson as a Consultant for Deep Learning and Large Language Models\" and the body \"Dear Mark Watson, I am writing to you to pitch the idea of hiring you as a consultant for deep learning and large language models. I believe you have the expertise and experience to help us achieve our goals. Please let me know if you are interested in discussing further. Thank you for your time.\"\n\n> Finished chain.\n```\n\n## Google Calendar Integration Example\n\nAssuming that you configured the Zapier Natural Language Action \"Google Calendar: Find Event\" then the same code we used to send an email in the last section works for checking calendar entries, you just need to change the natural language prompt:\n\n```python\nfrom langchain.llms import OpenAI\nfrom langchain.agents import initialize_agent\nfrom langchain.agents.agent_toolkits import ZapierToolkit\nfrom langchain.utilities.zapier import ZapierNLAWrapper\n\nllm = OpenAI(temperature=0)\nzapier = ZapierNLAWrapper()\ntoolkit = ZapierToolkit.from_zapier_nla_wrapper(zapier)\nagent = initialize_agent(toolkit.get_tools(), llm, \n                         agent=\"zero-shot-react-description\", verbose=True)\n\nagent.run(\"Get my Google Calendar entries for tomorrow\")\n```\n\nAnd the output looks like: \n\n```console\n$ python get_google_calendar.py\n\n> Entering new AgentExecutor chain...\n I need to find events in my Google Calendar\nAction: Google Calendar: Find Event\nAction Input: Find events in my Google Calendar tomorrow\nObservation: {\"location\": \"Greg to call Mark on (928) XXX-ZZZZ\", \"kind\": \"calendar#event\", \"end__dateTime\": \"2023-03-23T10:00:00-07:00\", \"status\": \"confirmed\", \"end__dateTime_pretty\": \"Mar 23, 2023 10:00AM\", \"htmlLink\": \"https://zpr.io/WWWWWWWW\"}\nThought: I now know the final answer\nFinal Answer: I have an event in my Google Calendar tomorrow at 10:00AM.\n\n> Finished chain.\n```\n\nI edited this output to remove some private information.\n"
  },
  {
    "path": "CHAPTERS_and_CODE_no_longer_in-book/zapier_integrations/README_code.md",
    "content": "# Using Zapier Integrations with GMail and Google Calendar\n\nZapier is a service for writing integrations with hundreds of cloud services. Here we will write some demos for writing automatic integrations with GMail and Google Calendar. I will also provide you, dear reader, with links for integrationg with Microsoft and other services.\n\n## Setup\n\nYou will need a developer key for [Zapier Natural Language Actions API](https://nla.zapier.com/get-started/). Go to this linked web page and look for \"Dev App\" in the \"Provider Name\" column. If a key does not exist, you'll need to setup an action to create a key. Click \"Setup Actions\" and follow the instructions. Your key will be in the Personal API Key column for the \"Dev App.\" Click to reveal and copy your key. You can [read the documentation](https://nla.zapier.com/api/v1/dynamic/docs).\n\n"
  },
  {
    "path": "CHAPTERS_and_CODE_no_longer_in-book/zapier_integrations/get_google_calendar.py",
    "content": "from langchain.llms import OpenAI\nfrom langchain.agents import initialize_agent\nfrom langchain.agents.agent_toolkits import ZapierToolkit\nfrom langchain.utilities.zapier import ZapierNLAWrapper\n\nllm = OpenAI(temperature=0)\nzapier = ZapierNLAWrapper()\ntoolkit = ZapierToolkit.from_zapier_nla_wrapper(zapier)\nagent = initialize_agent(toolkit.get_tools(), llm, \n                         agent=\"zero-shot-react-description\", verbose=True)\n\nagent.run(\"Get my Google Calendar entries for tomorrow\")\n"
  },
  {
    "path": "CHAPTERS_and_CODE_no_longer_in-book/zapier_integrations/requirements.txt",
    "content": "langchain\nlangchain_community\nopenai"
  },
  {
    "path": "CHAPTERS_and_CODE_no_longer_in-book/zapier_integrations/send_gmail.py",
    "content": "from langchain.llms import OpenAI\nfrom langchain.agents import initialize_agent\nfrom langchain.agents.agent_toolkits import ZapierToolkit\nfrom langchain.utilities.zapier import ZapierNLAWrapper\n\nllm = OpenAI(temperature=0)\nzapier = ZapierNLAWrapper()\ntoolkit = ZapierToolkit.from_zapier_nla_wrapper(zapier)\nagent = initialize_agent(toolkit.get_tools(), llm, \n                         agent=\"zero-shot-react-description\", verbose=True)\n\nagent.run(\"Send an Email to mark_watson99test.com via gmail that is a pitch for hiring Mark Watson as a consultant for deep learning and large language models\")\n"
  },
  {
    "path": "Makefile",
    "content": "install:\n\tpip install llama_index langchain trafilatura openai SPARQLWrapper rdflib rdflib-jsonld pydrive kor\n\nclean:\n\trm -r -f */*~ */#*\n\trm -r -f */venv */*/venv\n\trm -r -f */__pycache__ */*/__pycache__\n\n"
  },
  {
    "path": "README.md",
    "content": "# Example code for my book \"LangChain Project Lab Book: Hooking Large Language Models Up to the Real World\"\n\nYou can purchase this book on LeanPub and get free updates as new versions are released.\n\nThis book can be purchased or read free online at [https://leanpub.com/langchain](https://leanpub.com/langchain).\n\nI would like to thank readers who have purchased this book! I very much appreciate your support.\n\n## Older, non-supported, book chapters and code for these chapters now in directory CHAPTERS_and_CODE_no_longer_in-book\n\n### Starting in October 2024 I am removing older material from my book and archiving it. See the following index for older material:\n\n[CHAPTERS_and_CODE_no_longer_in-book/README.md](CHAPTERS_and_CODE_no_longer_in-book/README.md)\n\n"
  },
  {
    "path": "cooking_recipes/README.md",
    "content": "# Using LLMs to Generate Recipes\n\nUpdated 1/26/2024 for langchain-0.1.4 and using the new OpenAI embedding models\n\nSee the chapter in my book for information.\n\nSo far, this directory contain an initial experiment indexing my cookingspace.com recipe data and using LangChain and a prediction model to generate new recipes.\n\n## Recipe Copyrights\n\nMy JSON recipe files are Copyright 2010-2023 Mark Watson, as are the generated text file versions.\n\nWhat does copyright even mean for recipes? To the best of my knowledge, the only thing that can be copyright in recipes is the written cooking directions.\n"
  },
  {
    "path": "cooking_recipes/data/.gitignore",
    "content": "*~\n\n"
  },
  {
    "path": "cooking_recipes/data/correlated-ingredients.json",
    "content": "{\"chives\":\n {\"elbow macaroni\":2,\n  \"red wine\":3,\n  \"bacon\":18,\n  \"cayenne pepper\":8,\n  \"lemon juice\":48,\n  \"honey\":8,\n  \"swiss cheese\":7,\n  \"paprika\":26,\n  \"ground cinnamon\":2,\n  \"bread\":3,\n  \"yeast\":6,\n  \"egg yolks\":14,\n  \"green pepper\":5,\n  \"baking powder\":10,\n  \"saffron\":2,\n  \"sesame oil\":3,\n  \"cream of tartar\":2,\n  \"pine nuts\":8,\n  \"cinnamon\":3,\n  \"avocado\":3,\n  \"cheddar cheese\":12,\n  \"tomatoes\":23,\n  \"white pepper\":18,\n  \"brown rice\":2,\n  \"cauliflower\":3,\n  \"frozen peas\":2,\n  \"butter\":120,\n  \"salmon\":5,\n  \"orange juice\":3,\n  \"lemon\":11,\n  \"black pepper\":19,\n  \"black olives\":4,\n  \"buttermilk\":5,\n  \"capers\":7,\n  \"cornmeal\":2,\n  \"cloves\":6,\n  \"oil\":18,\n  \"chicken\":5,\n  \"blue cheese\":6,\n  \"cucumber\":7,\n  \"canola oil\":4,\n  \"dried tarragon\":5,\n  \"celery\":15,\n  \"walnuts\":5,\n  \"nutmeg\":13,\n  \"bread crumbs\":10,\n  \"baking soda\":4,\n  \"tabasco sauce\":6,\n  \"ginger\":5,\n  \"balsamic vinegar\":8,\n  \"fresh rosemary\":2,\n  \"lime\":3,\n  \"pecans\":3,\n  \"peanut butter\":2,\n  \"green onions\":9,\n  \"cumin\":4,\n  \"skim milk\":2,\n  \"beets\":2,\n  \"kidney beans\":2,\n  \"parmesan cheese\":15,\n  \"carrots\":16,\n  \"mushrooms\":22,\n  \"sour cream\":32,\n  \"yogurt\":3,\n  \"cream cheese\":29,\n  \"tofu\":3,\n  \"cherry tomatoes\":2,\n  \"salsa\":3,\n  \"parsley\":76,\n  \"green olives\":2,\n  \"rice vinegar\":2,\n  \"chili powder\":6,\n  \"spaghetti\":2,\n  \"red wine vinegar\":5,\n  \"cornstarch\":4,\n  \"orange\":4,\n  \"dried basil\":2,\n  \"radishes\":2,\n  \"catsup\":3,\n  \"ground beef\":4,\n  \"mozzarella cheese\":2,\n  \"almonds\":5,\n  \"eggs\":40,\n  \"margarine\":14,\n  \"fresh ginger\":2,\n  \"kale\":2,\n  \"garlic powder\":15,\n  \"dried thyme\":8,\n  \"whole wheat flour\":6,\n  \"salt\":167,\n  \"fresh basil\":5,\n  \"bean sprouts\":4,\n  \"potatoes\":20,\n  \"allspice\":2,\n  \"dijon mustard\":17,\n  \"soy sauce\":8,\n  \"broccoli\":4,\n  \"white rice\":2,\n  \"egg whites\":6,\n  \"worcestershire sauce\":9,\n  \"brown sugar\":2,\n  \"lime juice\":5,\n  \"dry mustard\":10,\n  \"white flour\":2,\n  \"mayonnaise\":19,\n  \"fresh thyme\":2,\n  \"vegetable oil\":14,\n  \"olive oil\":58,\n  \"curry powder\":9,\n  \"flour\":37,\n  \"vinegar\":7,\n  \"white wine\":11,\n  \"sherry\":3,\n  \"oats\":2,\n  \"whipping cream\":5,\n  \"sugar\":22},\n \"cumin\":\n {\"elbow macaroni\":5,\n  \"canned beef broth\":3,\n  \"red wine\":16,\n  \"canned tomato sauce\":5,\n  \"bacon\":16,\n  \"cayenne pepper\":133,\n  \"lemon juice\":120,\n  \"honey\":25,\n  \"swiss cheese\":3,\n  \"paprika\":177,\n  \"bread\":2,\n  \"ground cinnamon\":6,\n  \"yeast\":2,\n  \"green pepper\":109,\n  \"baking powder\":10,\n  \"saffron\":3,\n  \"sesame oil\":9,\n  \"pine nuts\":2,\n  \"cinnamon\":73,\n  \"avocado\":11,\n  \"cheddar cheese\":33,\n  \"corn tortilla\":2,\n  \"tomatoes\":177,\n  \"white pepper\":34,\n  \"brown rice\":17,\n  \"black beans\":43,\n  \"fresh garlic\":3,\n  \"pinto beans\":38,\n  \"cauliflower\":12,\n  \"frozen peas\":4,\n  \"butter\":63,\n  \"salmon\":2,\n  \"orange juice\":13,\n  \"black pepper\":171,\n  \"lemon\":23,\n  \"tumeric\":21,\n  \"black olives\":13,\n  \"buttermilk\":2,\n  \"powdered sugar\":2,\n  \"hoisin sauce\":2,\n  \"dried oregano\":28,\n  \"eggplant\":19,\n  \"capers\":8,\n  \"cornmeal\":14,\n  \"cloves\":49,\n  \"oil\":136,\n  \"chicken\":25,\n  \"cucumber\":12,\n  \"canola oil\":13,\n  \"dried tarragon\":2,\n  \"celery\":45,\n  \"alfalfa sprouts\":2,\n  \"fresh peaches\":3,\n  \"walnuts\":10,\n  \"nutmeg\":34,\n  \"fresh cilantro\":24,\n  \"baking soda\":6,\n  \"bread crumbs\":14,\n  \"corn syrup\":2,\n  \"tabasco sauce\":45,\n  \"ginger\":68,\n  \"balsamic vinegar\":8,\n  \"lime\":17,\n  \"pecans\":2,\n  \"peanut butter\":9,\n  \"green onions\":36,\n  \"frozen corn\":13,\n  \"banana\":2,\n  \"beets\":3,\n  \"skim milk\":11,\n  \"barley\":5,\n  \"kidney beans\":41,\n  \"shortening\":6,\n  \"applesauce\":2,\n  \"parmesan cheese\":5,\n  \"carrots\":65,\n  \"mushrooms\":25,\n  \"sour cream\":50,\n  \"yogurt\":25,\n  \"cream cheese\":12,\n  \"tofu\":10,\n  \"cherry tomatoes\":3,\n  \"salsa\":22,\n  \"parsley\":79,\n  \"green olives\":4,\n  \"rice vinegar\":3,\n  \"molasses\":11,\n  \"beer\":41,\n  \"chili powder\":403,\n  \"spaghetti\":3,\n  \"raisins\":29,\n  \"red wine vinegar\":15,\n  \"chives\":4,\n  \"garam masala\":19,\n  \"cornstarch\":14,\n  \"orange\":6,\n  \"dried basil\":6,\n  \"radishes\":2,\n  \"catsup\":11,\n  \"ground beef\":68,\n  \"mozzarella cheese\":8,\n  \"romaine lettuce\":2,\n  \"sweet red pepper\":4,\n  \"cumin seeds\":3,\n  \"barbecue sauce\":3,\n  \"red pepper flakes\":16,\n  \"almonds\":13,\n  \"eggs\":26,\n  \"margarine\":15,\n  \"fresh ginger\":7,\n  \"kale\":3,\n  \"garlic powder\":134,\n  \"dried thyme\":7,\n  \"cream of mushroom soup\":4,\n  \"whole wheat flour\":2,\n  \"salt\":625,\n  \"fresh basil\":3,\n  \"bean sprouts\":2,\n  \"potatoes\":29,\n  \"frozen spinach\":4,\n  \"celery seeds\":2,\n  \"allspice\":39,\n  \"dijon mustard\":13,\n  \"soy sauce\":38,\n  \"broccoli\":3,\n  \"apples\":4,\n  \"maple syrup\":4,\n  \"sweet potatoes\":6,\n  \"white rice\":5,\n  \"egg whites\":4,\n  \"worcestershire sauce\":35,\n  \"dried cilantro\":5,\n  \"brown sugar\":40,\n  \"lime juice\":52,\n  \"dry mustard\":32,\n  \"mayonnaise\":5,\n  \"chicken wings\":5,\n  \"vegetable oil\":123,\n  \"fresh thyme\":2,\n  \"olive oil\":257,\n  \"flour\":71,\n  \"curry powder\":50,\n  \"vinegar\":48,\n  \"white wine\":8,\n  \"sherry\":5,\n  \"oats\":4,\n  \"whipping cream\":2,\n  \"chicken breast\":11,\n  \"sugar\":93},\n \"white wine\":\n {\"red wine\":7,\n  \"bacon\":27,\n  \"cayenne pepper\":28,\n  \"lemon juice\":92,\n  \"honey\":29,\n  \"zucchini squash\":2,\n  \"swiss cheese\":17,\n  \"paprika\":54,\n  \"ground cinnamon\":5,\n  \"bread\":4,\n  \"yeast\":5,\n  \"egg yolks\":22,\n  \"green pepper\":21,\n  \"baking powder\":7,\n  \"saffron\":9,\n  \"sesame oil\":15,\n  \"cinnamon\":11,\n  \"pine nuts\":6,\n  \"cream of tartar\":2,\n  \"avocado\":3,\n  \"cheddar cheese\":4,\n  \"tomatoes\":49,\n  \"white pepper\":35,\n  \"brown rice\":4,\n  \"black beans\":6,\n  \"fresh garlic\":3,\n  \"cauliflower\":2,\n  \"frozen peas\":2,\n  \"butter\":241,\n  \"salmon\":5,\n  \"orange juice\":19,\n  \"lemon\":40,\n  \"black pepper\":58,\n  \"black olives\":8,\n  \"buttermilk\":3,\n  \"powdered sugar\":8,\n  \"eggplant\":2,\n  \"dried oregano\":16,\n  \"hoisin sauce\":3,\n  \"capers\":21,\n  \"cloves\":11,\n  \"oil\":60,\n  \"chicken\":31,\n  \"blue cheese\":2,\n  \"cucumber\":4,\n  \"canola oil\":3,\n  \"dried tarragon\":4,\n  \"strawberries\":8,\n  \"celery\":38,\n  \"fresh cilantro\":5,\n  \"nutmeg\":31,\n  \"bread crumbs\":23,\n  \"baking soda\":2,\n  \"corn syrup\":3,\n  \"tabasco sauce\":10,\n  \"ginger\":14,\n  \"balsamic vinegar\":11,\n  \"fresh rosemary\":9,\n  \"lime\":5,\n  \"peanut butter\":2,\n  \"green onions\":40,\n  \"cumin\":8,\n  \"canned chicken broth\":2,\n  \"banana\":2,\n  \"skim milk\":6,\n  \"barley\":2,\n  \"shortening\":5,\n  \"parmesan cheese\":43,\n  \"carrots\":39,\n  \"mushrooms\":72,\n  \"sour cream\":25,\n  \"yogurt\":2,\n  \"tofu\":2,\n  \"cream cheese\":3,\n  \"cherry tomatoes\":3,\n  \"salsa\":2,\n  \"parsley\":99,\n  \"green olives\":6,\n  \"rice vinegar\":3,\n  \"molasses\":2,\n  \"chili powder\":9,\n  \"spaghetti\":7,\n  \"raisins\":11,\n  \"red wine vinegar\":14,\n  \"chives\":11,\n  \"cornstarch\":56,\n  \"orange\":8,\n  \"dried basil\":8,\n  \"catsup\":8,\n  \"ground beef\":8,\n  \"mozzarella cheese\":7,\n  \"adzuki beans\":2,\n  \"sweet red pepper\":2,\n  \"barbecue sauce\":2,\n  \"red pepper flakes\":8,\n  \"almonds\":6,\n  \"eggs\":33,\n  \"margarine\":20,\n  \"fresh ginger\":3,\n  \"garlic powder\":21,\n  \"dried thyme\":14,\n  \"cream of mushroom soup\":10,\n  \"vanilla\":5,\n  \"whole wheat flour\":2,\n  \"salt\":342,\n  \"fresh basil\":14,\n  \"shiitake mushrooms\":5,\n  \"potatoes\":12,\n  \"almond extract\":4,\n  \"frozen spinach\":2,\n  \"allspice\":8,\n  \"dijon mustard\":22,\n  \"soy sauce\":46,\n  \"broccoli\":4,\n  \"apples\":9,\n  \"white rice\":3,\n  \"yellow mustard\":4,\n  \"maple syrup\":10,\n  \"egg whites\":5,\n  \"worcestershire sauce\":34,\n  \"brown sugar\":19,\n  \"lime juice\":12,\n  \"dry mustard\":15,\n  \"raw shrimp\":3,\n  \"blueberries\":2,\n  \"mayonnaise\":4,\n  \"chicken wings\":3,\n  \"fresh thyme\":9,\n  \"olive oil\":239,\n  \"vegetable oil\":40,\n  \"flour\":128,\n  \"curry powder\":11,\n  \"vinegar\":14,\n  \"sherry\":8,\n  \"whipping cream\":16,\n  \"chicken breast\":6,\n  \"sugar\":87},\n \"bread crumbs\":\n {\"elbow macaroni\":5,\n  \"red wine\":6,\n  \"bacon\":18,\n  \"cayenne pepper\":33,\n  \"lemon juice\":77,\n  \"honey\":8,\n  \"swiss cheese\":16,\n  \"paprika\":57,\n  \"bread\":4,\n  \"ground cinnamon\":7,\n  \"yeast\":2,\n  \"egg yolks\":22,\n  \"green pepper\":31,\n  \"baking powder\":9,\n  \"sesame oil\":3,\n  \"cream of tartar\":4,\n  \"pine nuts\":13,\n  \"cinnamon\":27,\n  \"avocado\":2,\n  \"cheddar cheese\":27,\n  \"tomatoes\":30,\n  \"white pepper\":26,\n  \"brown rice\":7,\n  \"black beans\":3,\n  \"pinto beans\":3,\n  \"fresh garlic\":2,\n  \"cauliflower\":3,\n  \"butter\":294,\n  \"frozen peas\":3,\n  \"salmon\":9,\n  \"orange juice\":6,\n  \"lemon\":23,\n  \"black pepper\":65,\n  \"black olives\":3,\n  \"buttermilk\":4,\n  \"eggplant\":23,\n  \"dried oregano\":14,\n  \"capers\":5,\n  \"cornmeal\":6,\n  \"cloves\":14,\n  \"oil\":36,\n  \"chicken\":10,\n  \"blue cheese\":2,\n  \"cucumber\":4,\n  \"canola oil\":8,\n  \"dried tarragon\":4,\n  \"celery\":39,\n  \"fresh cilantro\":2,\n  \"walnuts\":12,\n  \"fresh peaches\":2,\n  \"nutmeg\":44,\n  \"baking soda\":6,\n  \"tabasco sauce\":14,\n  \"balsamic vinegar\":8,\n  \"ginger\":16,\n  \"fresh rosemary\":2,\n  \"pecans\":5,\n  \"cumin\":14,\n  \"green onions\":18,\n  \"frozen corn\":4,\n  \"skim milk\":15,\n  \"kidney beans\":2,\n  \"shortening\":8,\n  \"parmesan cheese\":84,\n  \"applesauce\":2,\n  \"carrots\":18,\n  \"mushrooms\":58,\n  \"sour cream\":40,\n  \"yogurt\":2,\n  \"brussels sprouts\":2,\n  \"cream cheese\":16,\n  \"tofu\":12,\n  \"cherry tomatoes\":3,\n  \"parsley\":145,\n  \"green olives\":2,\n  \"rice vinegar\":2,\n  \"molasses\":3,\n  \"beer\":2,\n  \"chili powder\":9,\n  \"spaghetti\":2,\n  \"raisins\":14,\n  \"red wine vinegar\":3,\n  \"chives\":10,\n  \"cornstarch\":10,\n  \"orange\":4,\n  \"dried basil\":13,\n  \"radishes\":2,\n  \"catsup\":10,\n  \"ground beef\":76,\n  \"romaine lettuce\":2,\n  \"mozzarella cheese\":21,\n  \"sweet red pepper\":4,\n  \"cumin seeds\":2,\n  \"barbecue sauce\":2,\n  \"red pepper flakes\":2,\n  \"almonds\":6,\n  \"eggs\":192,\n  \"margarine\":55,\n  \"garlic powder\":41,\n  \"dried thyme\":7,\n  \"cream of mushroom soup\":11,\n  \"vanilla\":3,\n  \"whole wheat flour\":2,\n  \"salt\":415,\n  \"fresh basil\":4,\n  \"potatoes\":26,\n  \"frozen spinach\":5,\n  \"celery seeds\":2,\n  \"allspice\":12,\n  \"dijon mustard\":19,\n  \"soy sauce\":24,\n  \"broccoli\":3,\n  \"chicken legs\":2,\n  \"apples\":13,\n  \"sweet potatoes\":2,\n  \"white rice\":2,\n  \"shell macaroni\":2,\n  \"maple syrup\":2,\n  \"egg whites\":22,\n  \"worcestershire sauce\":63,\n  \"brown sugar\":19,\n  \"lime juice\":2,\n  \"dry mustard\":30,\n  \"mayonnaise\":33,\n  \"olive oil\":121,\n  \"vegetable oil\":37,\n  \"flour\":185,\n  \"curry powder\":22,\n  \"vinegar\":13,\n  \"white wine\":23,\n  \"sherry\":14,\n  \"oats\":4,\n  \"whipping cream\":6,\n  \"chicken breast\":4,\n  \"sugar\":42},\n \"butter\":\n {\"canned beef broth\":2,\n  \"elbow macaroni\":17,\n  \"red wine\":75,\n  \"canned tomato sauce\":3,\n  \"bacon\":156,\n  \"canned clams\":2,\n  \"brown onions\":3,\n  \"cayenne pepper\":218,\n  \"honey\":365,\n  \"lemon juice\":973,\n  \"zucchini squash\":3,\n  \"swiss cheese\":86,\n  \"paprika\":414,\n  \"ground cinnamon\":270,\n  \"bread\":93,\n  \"yeast\":326,\n  \"egg yolks\":536,\n  \"green pepper\":145,\n  \"baking powder\":1518,\n  \"saffron\":35,\n  \"sesame oil\":7,\n  \"cream of tartar\":120,\n  \"cinnamon\":1064,\n  \"pine nuts\":57,\n  \"avocado\":14,\n  \"cheddar cheese\":207,\n  \"tomatoes\":259,\n  \"white pepper\":192,\n  \"brown rice\":20,\n  \"black beans\":6,\n  \"pinto beans\":7,\n  \"fresh garlic\":8,\n  \"cauliflower\":15,\n  \"frozen peas\":25,\n  \"salmon\":26,\n  \"orange juice\":222,\n  \"lemon\":301,\n  \"black pepper\":341,\n  \"tumeric\":7,\n  \"black olives\":21,\n  \"buttermilk\":284,\n  \"powdered sugar\":421,\n  \"hoisin sauce\":3,\n  \"eggplant\":35,\n  \"dried oregano\":58,\n  \"capers\":58,\n  \"cornmeal\":74,\n  \"cloves\":198,\n  \"oil\":306,\n  \"chicken\":146,\n  \"blue cheese\":19,\n  \"cucumber\":13,\n  \"canola oil\":36,\n  \"flour tortilla\":4,\n  \"dried tarragon\":33,\n  \"strawberries\":52,\n  \"celery\":230,\n  \"alfalfa sprouts\":2,\n  \"cornish game hens\":12,\n  \"fresh peaches\":15,\n  \"fresh cilantro\":13,\n  \"walnuts\":282,\n  \"nutmeg\":710,\n  \"bread crumbs\":294,\n  \"baking soda\":1047,\n  \"tabasco sauce\":118,\n  \"corn syrup\":108,\n  \"balsamic vinegar\":43,\n  \"ginger\":134,\n  \"fresh rosemary\":17,\n  \"lime\":16,\n  \"pecans\":328,\n  \"peanut butter\":124,\n  \"frozen corn\":10,\n  \"cumin\":63,\n  \"green onions\":166,\n  \"canned chicken broth\":6,\n  \"beets\":14,\n  \"banana\":30,\n  \"skim milk\":34,\n  \"barley\":8,\n  \"kidney beans\":9,\n  \"shortening\":190,\n  \"parmesan cheese\":376,\n  \"applesauce\":40,\n  \"carrots\":280,\n  \"mushrooms\":440,\n  \"sour cream\":675,\n  \"yogurt\":38,\n  \"brussels sprouts\":7,\n  \"tofu\":6,\n  \"cream cheese\":451,\n  \"cherry tomatoes\":13,\n  \"salsa\":12,\n  \"parsley\":586,\n  \"green olives\":12,\n  \"rice vinegar\":7,\n  \"molasses\":181,\n  \"chili powder\":102,\n  \"beer\":51,\n  \"spaghetti\":24,\n  \"raisins\":451,\n  \"red wine vinegar\":52,\n  \"garam masala\":12,\n  \"chives\":120,\n  \"cornstarch\":407,\n  \"orange\":60,\n  \"dried basil\":62,\n  \"radishes\":6,\n  \"catsup\":41,\n  \"ground beef\":96,\n  \"adzuki beans\":2,\n  \"mozzarella cheese\":47,\n  \"sweet red pepper\":22,\n  \"cumin seeds\":14,\n  \"canned peaches\":2,\n  \"barbecue sauce\":6,\n  \"raw spinach\":3,\n  \"red pepper flakes\":19,\n  \"eggs\":2909,\n  \"almonds\":187,\n  \"margarine\":39,\n  \"fresh ginger\":8,\n  \"kale\":7,\n  \"garlic powder\":144,\n  \"dried thyme\":111,\n  \"cream of mushroom soup\":56,\n  \"low fat milk\":4,\n  \"vanilla\":1845,\n  \"whole wheat flour\":131,\n  \"salt\":6434,\n  \"fresh basil\":45,\n  \"bean sprouts\":4,\n  \"shiitake mushrooms\":13,\n  \"potatoes\":325,\n  \"almond extract\":163,\n  \"frozen spinach\":27,\n  \"celery seeds\":6,\n  \"allspice\":98,\n  \"dijon mustard\":131,\n  \"soy sauce\":108,\n  \"broccoli\":40,\n  \"chicken legs\":5,\n  \"apples\":187,\n  \"shell macaroni\":2,\n  \"white rice\":16,\n  \"yellow mustard\":3,\n  \"maple syrup\":98,\n  \"sweet potatoes\":54,\n  \"egg whites\":221,\n  \"worcestershire sauce\":324,\n  \"dried cilantro\":2,\n  \"brown sugar\":1081,\n  \"lime juice\":43,\n  \"dry mustard\":171,\n  \"raw shrimp\":15,\n  \"white flour\":30,\n  \"blueberries\":61,\n  \"mayonnaise\":103,\n  \"chicken wings\":26,\n  \"fresh thyme\":22,\n  \"vegetable oil\":220,\n  \"olive oil\":605,\n  \"flour\":4221,\n  \"curry powder\":150,\n  \"vinegar\":165,\n  \"white wine\":241,\n  \"sherry\":153,\n  \"yam\":2,\n  \"oats\":53,\n  \"whipping cream\":364,\n  \"chicken breast\":19,\n  \"sugar\":4965},\n \"canned tomato paste\":\n {\"worcestershire sauce\":2,\n  \"flour\":3,\n  \"garlic powder\":2,\n  \"tomatoes\":2,\n  \"lemon juice\":2,\n  \"salt\":3},\n \"fresh garlic\":\n {\"sour cream\":2,\n  \"red wine\":2,\n  \"cherry tomatoes\":4,\n  \"salsa\":2,\n  \"parsley\":2,\n  \"cayenne pepper\":2,\n  \"honey\":4,\n  \"lemon juice\":3,\n  \"beer\":2,\n  \"paprika\":2,\n  \"raisins\":2,\n  \"yeast\":2,\n  \"green pepper\":2,\n  \"baking powder\":2,\n  \"dried basil\":4,\n  \"sesame oil\":2,\n  \"catsup\":2,\n  \"pine nuts\":2,\n  \"cheddar cheese\":3,\n  \"mozzarella cheese\":4,\n  \"romaine lettuce\":2,\n  \"tomatoes\":2,\n  \"white pepper\":2,\n  \"eggs\":3,\n  \"margarine\":2,\n  \"fresh ginger\":7,\n  \"frozen peas\":2,\n  \"butter\":8,\n  \"garlic powder\":3,\n  \"dried thyme\":4,\n  \"orange juice\":2,\n  \"salt\":19,\n  \"black pepper\":14,\n  \"fresh basil\":3,\n  \"tumeric\":2,\n  \"black olives\":2,\n  \"buttermilk\":2,\n  \"dried oregano\":5,\n  \"hoisin sauce\":2,\n  \"oil\":2,\n  \"canola oil\":3,\n  \"dijon mustard\":2,\n  \"soy sauce\":7,\n  \"fresh cilantro\":2,\n  \"bread crumbs\":2,\n  \"worcestershire sauce\":4,\n  \"tabasco sauce\":2,\n  \"brown sugar\":3,\n  \"ginger\":3,\n  \"balsamic vinegar\":3,\n  \"lime juice\":2,\n  \"dry mustard\":4,\n  \"fresh rosemary\":2,\n  \"peanut butter\":2,\n  \"green onions\":4,\n  \"cumin\":3,\n  \"vegetable oil\":5,\n  \"olive oil\":15,\n  \"skim milk\":2,\n  \"beets\":2,\n  \"barley\":2,\n  \"curry powder\":2,\n  \"flour\":4,\n  \"white wine\":3,\n  \"parmesan cheese\":4,\n  \"carrots\":3,\n  \"sherry\":2,\n  \"chicken breast\":2,\n  \"mushrooms\":2,\n  \"sugar\":9},\n \"brussels sprouts\":\n {\"potatoes\":3,\n  \"bacon\":3,\n  \"dried oregano\":2,\n  \"cayenne pepper\":4,\n  \"parsley\":3,\n  \"honey\":2,\n  \"lemon juice\":2,\n  \"chicken\":2,\n  \"beer\":3,\n  \"canola oil\":3,\n  \"dijon mustard\":4,\n  \"soy sauce\":2,\n  \"raisins\":3,\n  \"celery\":2,\n  \"broccoli\":2,\n  \"red wine vinegar\":3,\n  \"walnuts\":2,\n  \"nutmeg\":2,\n  \"bread crumbs\":2,\n  \"cornstarch\":4,\n  \"sesame oil\":2,\n  \"tabasco sauce\":2,\n  \"balsamic vinegar\":2,\n  \"cheddar cheese\":2,\n  \"tomatoes\":2,\n  \"cumin seeds\":4,\n  \"green onions\":3,\n  \"olive oil\":2,\n  \"flour\":2,\n  \"barley\":2,\n  \"margarine\":2,\n  \"butter\":7,\n  \"dried thyme\":3,\n  \"carrots\":3,\n  \"sugar\":2,\n  \"salt\":11},\n \"beets\":\n {\"sour cream\":11,\n  \"yogurt\":2,\n  \"cream cheese\":3,\n  \"parsley\":6,\n  \"green olives\":2,\n  \"rice vinegar\":2,\n  \"lemon juice\":19,\n  \"honey\":7,\n  \"swiss cheese\":2,\n  \"paprika\":2,\n  \"ground cinnamon\":4,\n  \"raisins\":3,\n  \"red wine vinegar\":12,\n  \"chives\":2,\n  \"egg yolks\":4,\n  \"cornstarch\":4,\n  \"baking powder\":2,\n  \"dried basil\":3,\n  \"catsup\":2,\n  \"cinnamon\":7,\n  \"ground beef\":2,\n  \"cheddar cheese\":2,\n  \"tomatoes\":3,\n  \"white pepper\":3,\n  \"eggs\":13,\n  \"fresh garlic\":2,\n  \"margarine\":3,\n  \"fresh ginger\":2,\n  \"cauliflower\":2,\n  \"butter\":14,\n  \"garlic powder\":3,\n  \"dried thyme\":2,\n  \"vanilla\":4,\n  \"orange juice\":4,\n  \"black pepper\":3,\n  \"salt\":53,\n  \"lemon\":5,\n  \"shiitake mushrooms\":2,\n  \"buttermilk\":2,\n  \"potatoes\":12,\n  \"powdered sugar\":3,\n  \"dried oregano\":2,\n  \"capers\":4,\n  \"cloves\":6,\n  \"oil\":8,\n  \"cucumber\":3,\n  \"allspice\":4,\n  \"celery seeds\":2,\n  \"canola oil\":2,\n  \"dijon mustard\":5,\n  \"dried tarragon\":2,\n  \"soy sauce\":2,\n  \"celery\":3,\n  \"apples\":2,\n  \"walnuts\":3,\n  \"egg whites\":2,\n  \"baking soda\":3,\n  \"brown sugar\":9,\n  \"balsamic vinegar\":4,\n  \"dry mustard\":3,\n  \"lime\":2,\n  \"mayonnaise\":3,\n  \"green onions\":2,\n  \"cumin\":3,\n  \"olive oil\":19,\n  \"vegetable oil\":9,\n  \"flour\":8,\n  \"vinegar\":17,\n  \"parmesan cheese\":3,\n  \"carrots\":24,\n  \"sherry\":2,\n  \"oats\":2,\n  \"chicken breast\":2,\n  \"sugar\":46,\n  \"mushrooms\":2},\n \"sweet red pepper\":\n {\"red wine\":2,\n  \"bacon\":4,\n  \"cayenne pepper\":6,\n  \"lemon juice\":20,\n  \"honey\":3,\n  \"paprika\":6,\n  \"bread\":2,\n  \"green pepper\":24,\n  \"baking powder\":6,\n  \"saffron\":4,\n  \"sesame oil\":9,\n  \"cinnamon\":5,\n  \"avocado\":2,\n  \"cheddar cheese\":6,\n  \"tomatoes\":13,\n  \"white pepper\":5,\n  \"brown rice\":4,\n  \"black beans\":4,\n  \"pinto beans\":2,\n  \"cauliflower\":4,\n  \"butter\":22,\n  \"frozen peas\":3,\n  \"orange juice\":3,\n  \"lemon\":2,\n  \"black pepper\":14,\n  \"tumeric\":2,\n  \"buttermilk\":4,\n  \"hoisin sauce\":2,\n  \"dried oregano\":5,\n  \"eggplant\":5,\n  \"capers\":2,\n  \"cornmeal\":3,\n  \"cloves\":2,\n  \"oil\":6,\n  \"chicken\":4,\n  \"cucumber\":4,\n  \"celery\":11,\n  \"fresh cilantro\":5,\n  \"nutmeg\":4,\n  \"baking soda\":2,\n  \"bread crumbs\":4,\n  \"ginger\":2,\n  \"balsamic vinegar\":5,\n  \"fresh rosemary\":2,\n  \"lime\":2,\n  \"peanut butter\":4,\n  \"green onions\":25,\n  \"frozen corn\":2,\n  \"cumin\":4,\n  \"canned chicken broth\":3,\n  \"skim milk\":2,\n  \"kidney beans\":2,\n  \"parmesan cheese\":7,\n  \"carrots\":15,\n  \"mushrooms\":14,\n  \"sour cream\":4,\n  \"tofu\":2,\n  \"cream cheese\":3,\n  \"salsa\":3,\n  \"parsley\":7,\n  \"rice vinegar\":2,\n  \"beer\":2,\n  \"chili powder\":13,\n  \"raisins\":4,\n  \"red wine vinegar\":5,\n  \"cornstarch\":8,\n  \"dried basil\":9,\n  \"catsup\":2,\n  \"ground beef\":3,\n  \"red pepper flakes\":3,\n  \"eggs\":11,\n  \"margarine\":4,\n  \"fresh ginger\":3,\n  \"garlic powder\":4,\n  \"dried thyme\":5,\n  \"salt\":89,\n  \"fresh basil\":9,\n  \"bean sprouts\":2,\n  \"potatoes\":5,\n  \"celery seeds\":4,\n  \"allspice\":2,\n  \"dijon mustard\":10,\n  \"soy sauce\":12,\n  \"broccoli\":3,\n  \"apples\":2,\n  \"white rice\":2,\n  \"sweet potatoes\":2,\n  \"egg whites\":2,\n  \"worcestershire sauce\":3,\n  \"dried cilantro\":2,\n  \"brown sugar\":4,\n  \"lime juice\":8,\n  \"dry mustard\":8,\n  \"raw shrimp\":2,\n  \"mayonnaise\":5,\n  \"olive oil\":53,\n  \"vegetable oil\":31,\n  \"curry powder\":7,\n  \"flour\":8,\n  \"vinegar\":9,\n  \"white wine\":2,\n  \"sherry\":2,\n  \"chicken breast\":4,\n  \"sugar\":23},\n \"cumin seeds\":\n {\"red wine\":3,\n  \"coconut oil\":2,\n  \"brown onions\":2,\n  \"cayenne pepper\":38,\n  \"lemon juice\":37,\n  \"honey\":3,\n  \"zucchini squash\":2,\n  \"paprika\":26,\n  \"ground cinnamon\":11,\n  \"bread\":2,\n  \"green pepper\":4,\n  \"baking powder\":2,\n  \"saffron\":5,\n  \"sesame oil\":2,\n  \"cinnamon\":15,\n  \"pine nuts\":3,\n  \"avocado\":2,\n  \"cheddar cheese\":2,\n  \"tomatoes\":34,\n  \"white pepper\":3,\n  \"brown rice\":3,\n  \"black beans\":5,\n  \"pinto beans\":3,\n  \"cauliflower\":3,\n  \"frozen peas\":3,\n  \"butter\":14,\n  \"salmon\":2,\n  \"orange juice\":4,\n  \"lemon\":4,\n  \"black pepper\":22,\n  \"tumeric\":12,\n  \"dried oregano\":11,\n  \"eggplant\":5,\n  \"capers\":2,\n  \"cloves\":37,\n  \"oil\":29,\n  \"chicken\":8,\n  \"blue cheese\":3,\n  \"canola oil\":6,\n  \"fresh cilantro\":7,\n  \"nutmeg\":12,\n  \"bread crumbs\":2,\n  \"baking soda\":4,\n  \"tabasco sauce\":3,\n  \"ginger\":21,\n  \"lime\":3,\n  \"peanut butter\":2,\n  \"cumin\":3,\n  \"green onions\":8,\n  \"banana\":2,\n  \"kidney beans\":3,\n  \"shortening\":2,\n  \"parmesan cheese\":2,\n  \"carrots\":10,\n  \"mushrooms\":5,\n  \"sour cream\":5,\n  \"yogurt\":13,\n  \"brussels sprouts\":4,\n  \"cherry tomatoes\":2,\n  \"salsa\":2,\n  \"parsley\":4,\n  \"green olives\":2,\n  \"molasses\":3,\n  \"beer\":3,\n  \"chili powder\":27,\n  \"spaghetti\":3,\n  \"raisins\":8,\n  \"red wine vinegar\":9,\n  \"garam masala\":33,\n  \"cornstarch\":2,\n  \"orange\":2,\n  \"ground beef\":2,\n  \"mozzarella cheese\":2,\n  \"romaine lettuce\":3,\n  \"eggs\":5,\n  \"almonds\":4,\n  \"fresh ginger\":10,\n  \"garlic powder\":5,\n  \"dried thyme\":2,\n  \"salt\":193,\n  \"potatoes\":19,\n  \"frozen spinach\":2,\n  \"celery seeds\":3,\n  \"allspice\":2,\n  \"dijon mustard\":2,\n  \"soy sauce\":2,\n  \"maple syrup\":2,\n  \"white rice\":3,\n  \"worcestershire sauce\":3,\n  \"brown sugar\":9,\n  \"lime juice\":9,\n  \"dry mustard\":3,\n  \"white flour\":2,\n  \"vegetable oil\":35,\n  \"olive oil\":42,\n  \"flour\":8,\n  \"curry powder\":10,\n  \"vinegar\":5,\n  \"whipping cream\":4,\n  \"chicken breast\":3,\n  \"sugar\":25},\n \"celery\":\n {\"elbow macaroni\":7,\n  \"red wine\":11,\n  \"canned tomato sauce\":2,\n  \"bacon\":59,\n  \"cayenne pepper\":70,\n  \"lemon juice\":128,\n  \"honey\":20,\n  \"swiss cheese\":12,\n  \"paprika\":82,\n  \"bread\":20,\n  \"ground cinnamon\":7,\n  \"yeast\":7,\n  \"egg yolks\":5,\n  \"green pepper\":219,\n  \"baking powder\":12,\n  \"saffron\":4,\n  \"sesame oil\":23,\n  \"cinnamon\":22,\n  \"pine nuts\":4,\n  \"avocado\":2,\n  \"cheddar cheese\":46,\n  \"tomatoes\":157,\n  \"white pepper\":46,\n  \"brown rice\":27,\n  \"black beans\":15,\n  \"pinto beans\":13,\n  \"cauliflower\":20,\n  \"frozen peas\":16,\n  \"butter\":230,\n  \"salmon\":7,\n  \"orange juice\":16,\n  \"black pepper\":134,\n  \"lemon\":28,\n  \"tumeric\":4,\n  \"black olives\":19,\n  \"buttermilk\":2,\n  \"dried oregano\":18,\n  \"eggplant\":9,\n  \"capers\":13,\n  \"cornmeal\":5,\n  \"cloves\":13,\n  \"oil\":159,\n  \"chicken\":93,\n  \"blue cheese\":4,\n  \"cucumber\":17,\n  \"canola oil\":7,\n  \"dried tarragon\":8,\n  \"cornish game hens\":3,\n  \"alfalfa sprouts\":4,\n  \"fresh cilantro\":9,\n  \"walnuts\":33,\n  \"nutmeg\":28,\n  \"baking soda\":2,\n  \"bread crumbs\":39,\n  \"tabasco sauce\":36,\n  \"balsamic vinegar\":5,\n  \"ginger\":33,\n  \"fresh rosemary\":2,\n  \"lime\":6,\n  \"pecans\":19,\n  \"peanut butter\":5,\n  \"frozen corn\":3,\n  \"green onions\":101,\n  \"cumin\":45,\n  \"canned chicken broth\":2,\n  \"beets\":3,\n  \"skim milk\":10,\n  \"barley\":23,\n  \"kidney beans\":25,\n  \"shortening\":11,\n  \"applesauce\":2,\n  \"parmesan cheese\":34,\n  \"carrots\":279,\n  \"mushrooms\":118,\n  \"sour cream\":58,\n  \"yogurt\":8,\n  \"brussels sprouts\":2,\n  \"tofu\":11,\n  \"cream cheese\":20,\n  \"cherry tomatoes\":5,\n  \"salsa\":2,\n  \"parsley\":251,\n  \"green olives\":10,\n  \"rice vinegar\":8,\n  \"molasses\":8,\n  \"chili powder\":78,\n  \"beer\":11,\n  \"spaghetti\":16,\n  \"raisins\":27,\n  \"red wine vinegar\":21,\n  \"chives\":15,\n  \"cornstarch\":98,\n  \"orange\":5,\n  \"dried basil\":23,\n  \"radishes\":10,\n  \"catsup\":23,\n  \"ground beef\":49,\n  \"romaine lettuce\":2,\n  \"mozzarella cheese\":6,\n  \"sweet red pepper\":11,\n  \"canned tuna\":2,\n  \"canned peaches\":2,\n  \"barbecue sauce\":2,\n  \"red pepper flakes\":11,\n  \"eggs\":104,\n  \"almonds\":22,\n  \"margarine\":68,\n  \"fresh ginger\":5,\n  \"kale\":4,\n  \"garlic powder\":62,\n  \"dried thyme\":39,\n  \"cream of mushroom soup\":35,\n  \"low fat milk\":2,\n  \"vanilla\":2,\n  \"salt\":797,\n  \"fresh basil\":10,\n  \"bean sprouts\":21,\n  \"shiitake mushrooms\":5,\n  \"potatoes\":103,\n  \"frozen spinach\":2,\n  \"celery seeds\":5,\n  \"allspice\":12,\n  \"dijon mustard\":23,\n  \"soy sauce\":144,\n  \"broccoli\":20,\n  \"apples\":22,\n  \"shell macaroni\":4,\n  \"white rice\":4,\n  \"yellow mustard\":4,\n  \"sweet potatoes\":5,\n  \"egg whites\":11,\n  \"worcestershire sauce\":93,\n  \"dried cilantro\":2,\n  \"brown sugar\":40,\n  \"lime juice\":6,\n  \"dry mustard\":32,\n  \"raw shrimp\":2,\n  \"mayonnaise\":127,\n  \"chicken wings\":5,\n  \"olive oil\":123,\n  \"vegetable oil\":82,\n  \"fresh thyme\":8,\n  \"flour\":185,\n  \"curry powder\":53,\n  \"vinegar\":91,\n  \"white wine\":38,\n  \"sherry\":43,\n  \"oats\":2,\n  \"whipping cream\":14,\n  \"chicken breast\":15,\n  \"sugar\":199},\n \"nutmeg\":\n {\"elbow macaroni\":3,\n  \"red wine\":11,\n  \"canned tomato sauce\":4,\n  \"bacon\":27,\n  \"brown onions\":2,\n  \"cayenne pepper\":56,\n  \"honey\":109,\n  \"lemon juice\":176,\n  \"swiss cheese\":14,\n  \"paprika\":73,\n  \"ground cinnamon\":37,\n  \"bread\":15,\n  \"yeast\":46,\n  \"egg yolks\":83,\n  \"green pepper\":18,\n  \"baking powder\":423,\n  \"saffron\":8,\n  \"sesame oil\":3,\n  \"pine nuts\":11,\n  \"cinnamon\":1126,\n  \"cream of tartar\":20,\n  \"cheddar cheese\":18,\n  \"tomatoes\":25,\n  \"white pepper\":62,\n  \"brown rice\":5,\n  \"black beans\":3,\n  \"pinto beans\":3,\n  \"cauliflower\":6,\n  \"frozen peas\":5,\n  \"butter\":710,\n  \"salmon\":3,\n  \"orange juice\":69,\n  \"lemon\":39,\n  \"black pepper\":73,\n  \"tumeric\":6,\n  \"black olives\":2,\n  \"buttermilk\":55,\n  \"powdered sugar\":42,\n  \"dried oregano\":7,\n  \"eggplant\":10,\n  \"capers\":6,\n  \"cornmeal\":13,\n  \"cloves\":309,\n  \"oil\":117,\n  \"chicken\":17,\n  \"blue cheese\":3,\n  \"cucumber\":4,\n  \"dry rosemary\":2,\n  \"canola oil\":17,\n  \"dried tarragon\":4,\n  \"strawberries\":14,\n  \"celery\":28,\n  \"cornish game hens\":2,\n  \"alfalfa sprouts\":2,\n  \"fresh cilantro\":3,\n  \"fresh peaches\":13,\n  \"walnuts\":93,\n  \"bread crumbs\":44,\n  \"baking soda\":404,\n  \"tabasco sauce\":5,\n  \"corn syrup\":11,\n  \"balsamic vinegar\":2,\n  \"ginger\":196,\n  \"fresh rosemary\":2,\n  \"lime\":8,\n  \"pecans\":53,\n  \"peanut butter\":14,\n  \"green onions\":16,\n  \"cumin\":34,\n  \"skim milk\":43,\n  \"banana\":10,\n  \"barley\":3,\n  \"kidney beans\":2,\n  \"shortening\":126,\n  \"parmesan cheese\":78,\n  \"applesauce\":66,\n  \"carrots\":55,\n  \"mushrooms\":55,\n  \"sour cream\":69,\n  \"yogurt\":9,\n  \"brussels sprouts\":2,\n  \"tofu\":2,\n  \"cream cheese\":42,\n  \"parsley\":52,\n  \"green olives\":3,\n  \"rice vinegar\":2,\n  \"molasses\":98,\n  \"chili powder\":18,\n  \"beer\":9,\n  \"spaghetti\":7,\n  \"raisins\":308,\n  \"red wine vinegar\":9,\n  \"garam masala\":5,\n  \"chives\":13,\n  \"cornstarch\":84,\n  \"dried basil\":6,\n  \"orange\":15,\n  \"catsup\":9,\n  \"ground beef\":34,\n  \"romaine lettuce\":2,\n  \"mozzarella cheese\":21,\n  \"sweet red pepper\":4,\n  \"cumin seeds\":12,\n  \"canned peaches\":2,\n  \"red pepper flakes\":5,\n  \"eggs\":697,\n  \"almonds\":30,\n  \"margarine\":121,\n  \"fresh ginger\":4,\n  \"garlic powder\":26,\n  \"dried thyme\":14,\n  \"cream of mushroom soup\":4,\n  \"low fat milk\":2,\n  \"vanilla\":325,\n  \"whole wheat flour\":47,\n  \"salt\":1340,\n  \"fresh basil\":6,\n  \"bean sprouts\":2,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":44,\n  \"almond extract\":16,\n  \"frozen spinach\":16,\n  \"celery seeds\":5,\n  \"allspice\":231,\n  \"dijon mustard\":16,\n  \"soy sauce\":24,\n  \"broccoli\":9,\n  \"chicken legs\":3,\n  \"apples\":108,\n  \"yellow mustard\":2,\n  \"white rice\":2,\n  \"maple syrup\":40,\n  \"sweet potatoes\":36,\n  \"egg whites\":77,\n  \"worcestershire sauce\":11,\n  \"brown sugar\":371,\n  \"lime juice\":12,\n  \"dry mustard\":27,\n  \"white flour\":11,\n  \"blueberries\":16,\n  \"mayonnaise\":5,\n  \"chicken wings\":2,\n  \"fresh thyme\":6,\n  \"olive oil\":107,\n  \"vegetable oil\":93,\n  \"flour\":785,\n  \"curry powder\":13,\n  \"vinegar\":31,\n  \"white wine\":31,\n  \"sherry\":20,\n  \"yam\":2,\n  \"oats\":18,\n  \"whipping cream\":51,\n  \"sugar\":955},\n \"black beans\":\n {\"red wine\":4,\n  \"elbow macaroni\":2,\n  \"bacon\":10,\n  \"brown onions\":2,\n  \"cayenne pepper\":23,\n  \"lemon juice\":10,\n  \"honey\":11,\n  \"paprika\":7,\n  \"ground cinnamon\":4,\n  \"green pepper\":27,\n  \"baking powder\":4,\n  \"sesame oil\":5,\n  \"cinnamon\":7,\n  \"avocado\":5,\n  \"cheddar cheese\":7,\n  \"tomatoes\":32,\n  \"white pepper\":4,\n  \"brown rice\":7,\n  \"pinto beans\":10,\n  \"butter\":6,\n  \"salmon\":2,\n  \"orange juice\":3,\n  \"black pepper\":21,\n  \"lemon\":5,\n  \"black olives\":4,\n  \"dried oregano\":8,\n  \"hoisin sauce\":3,\n  \"capers\":2,\n  \"cloves\":5,\n  \"oil\":26,\n  \"chicken\":2,\n  \"cucumber\":2,\n  \"canola oil\":4,\n  \"celery\":15,\n  \"fresh cilantro\":23,\n  \"nutmeg\":3,\n  \"baking soda\":2,\n  \"bread crumbs\":3,\n  \"tabasco sauce\":7,\n  \"balsamic vinegar\":7,\n  \"ginger\":6,\n  \"lime\":6,\n  \"green onions\":19,\n  \"cumin\":43,\n  \"frozen corn\":6,\n  \"skim milk\":3,\n  \"barley\":2,\n  \"kidney beans\":12,\n  \"parmesan cheese\":3,\n  \"carrots\":16,\n  \"mushrooms\":4,\n  \"sour cream\":15,\n  \"cream cheese\":4,\n  \"cherry tomatoes\":3,\n  \"salsa\":12,\n  \"parsley\":9,\n  \"green olives\":3,\n  \"rice vinegar\":2,\n  \"molasses\":3,\n  \"chili powder\":58,\n  \"beer\":5,\n  \"red wine vinegar\":11,\n  \"cornstarch\":4,\n  \"dried basil\":6,\n  \"orange\":3,\n  \"ground beef\":3,\n  \"adzuki beans\":2,\n  \"mozzarella cheese\":2,\n  \"sweet red pepper\":4,\n  \"cumin seeds\":5,\n  \"barbecue sauce\":2,\n  \"red pepper flakes\":6,\n  \"eggs\":5,\n  \"almonds\":2,\n  \"margarine\":2,\n  \"fresh ginger\":3,\n  \"garlic powder\":9,\n  \"dried thyme\":4,\n  \"whole wheat flour\":2,\n  \"salt\":114,\n  \"fresh basil\":2,\n  \"sriracha hot chili sauce\":2,\n  \"potatoes\":4,\n  \"allspice\":4,\n  \"soy sauce\":9,\n  \"white rice\":5,\n  \"egg whites\":3,\n  \"brown sugar\":4,\n  \"lime juice\":19,\n  \"dry mustard\":3,\n  \"olive oil\":59,\n  \"vegetable oil\":34,\n  \"fresh thyme\":2,\n  \"flour\":5,\n  \"curry powder\":4,\n  \"vinegar\":8,\n  \"white wine\":6,\n  \"sherry\":3,\n  \"whipping cream\":2,\n  \"chicken breast\":2,\n  \"sugar\":14},\n \"egg yolks\":\n {\"elbow macaroni\":2,\n  \"red wine\":9,\n  \"bacon\":14,\n  \"cayenne pepper\":27,\n  \"lemon juice\":188,\n  \"honey\":29,\n  \"swiss cheese\":7,\n  \"paprika\":27,\n  \"bread\":8,\n  \"ground cinnamon\":30,\n  \"yeast\":11,\n  \"green pepper\":8,\n  \"baking powder\":152,\n  \"saffron\":4,\n  \"sesame oil\":4,\n  \"pine nuts\":4,\n  \"cinnamon\":75,\n  \"cream of tartar\":74,\n  \"cheddar cheese\":12,\n  \"tomatoes\":9,\n  \"white pepper\":22,\n  \"brown rice\":2,\n  \"cauliflower\":2,\n  \"butter\":536,\n  \"salmon\":3,\n  \"orange juice\":29,\n  \"black pepper\":21,\n  \"lemon\":71,\n  \"buttermilk\":18,\n  \"powdered sugar\":74,\n  \"eggplant\":4,\n  \"hoisin sauce\":3,\n  \"dried oregano\":4,\n  \"capers\":14,\n  \"cornmeal\":11,\n  \"cloves\":16,\n  \"oil\":36,\n  \"chicken\":9,\n  \"cucumber\":3,\n  \"canola oil\":6,\n  \"dried tarragon\":2,\n  \"strawberries\":27,\n  \"celery\":5,\n  \"alfalfa sprouts\":3,\n  \"nutmeg\":83,\n  \"fresh peaches\":2,\n  \"walnuts\":8,\n  \"bread crumbs\":22,\n  \"baking soda\":36,\n  \"corn syrup\":3,\n  \"tabasco sauce\":5,\n  \"ginger\":9,\n  \"balsamic vinegar\":3,\n  \"pecans\":24,\n  \"peanut butter\":9,\n  \"green onions\":9,\n  \"skim milk\":13,\n  \"beets\":4,\n  \"kidney beans\":2,\n  \"shortening\":21,\n  \"applesauce\":3,\n  \"parmesan cheese\":25,\n  \"carrots\":9,\n  \"mushrooms\":24,\n  \"sour cream\":64,\n  \"yogurt\":6,\n  \"cream cheese\":39,\n  \"cherry tomatoes\":2,\n  \"parsley\":35,\n  \"green olives\":2,\n  \"molasses\":5,\n  \"chili powder\":3,\n  \"beer\":9,\n  \"spaghetti\":2,\n  \"raisins\":35,\n  \"red wine vinegar\":9,\n  \"chives\":14,\n  \"cornstarch\":124,\n  \"orange\":10,\n  \"dried basil\":3,\n  \"catsup\":2,\n  \"ground beef\":9,\n  \"mozzarella cheese\":2,\n  \"canned tuna\":2,\n  \"red pepper flakes\":4,\n  \"eggs\":148,\n  \"almonds\":28,\n  \"margarine\":44,\n  \"fresh ginger\":3,\n  \"garlic powder\":5,\n  \"dried thyme\":4,\n  \"vanilla\":320,\n  \"whole wheat flour\":2,\n  \"salt\":718,\n  \"fresh basil\":4,\n  \"shiitake mushrooms\":3,\n  \"potatoes\":9,\n  \"almond extract\":30,\n  \"celery seeds\":2,\n  \"allspice\":11,\n  \"dijon mustard\":19,\n  \"soy sauce\":4,\n  \"broccoli\":2,\n  \"apples\":10,\n  \"yellow mustard\":2,\n  \"white rice\":2,\n  \"maple syrup\":15,\n  \"sweet potatoes\":5,\n  \"egg whites\":368,\n  \"worcestershire sauce\":15,\n  \"brown sugar\":61,\n  \"lime juice\":13,\n  \"dry mustard\":25,\n  \"white flour\":2,\n  \"blueberries\":8,\n  \"mayonnaise\":7,\n  \"vegetable oil\":35,\n  \"olive oil\":45,\n  \"flour\":388,\n  \"curry powder\":7,\n  \"vinegar\":23,\n  \"white wine\":22,\n  \"sherry\":28,\n  \"whipping cream\":186,\n  \"chicken breast\":3,\n  \"sugar\":1122},\n \"bacon\":\n {\"elbow macaroni\":6,\n  \"red wine\":19,\n  \"canned beef broth\":2,\n  \"cayenne pepper\":34,\n  \"honey\":8,\n  \"lemon juice\":46,\n  \"swiss cheese\":26,\n  \"paprika\":50,\n  \"ground cinnamon\":5,\n  \"bread\":8,\n  \"yeast\":3,\n  \"egg yolks\":14,\n  \"green pepper\":57,\n  \"baking powder\":21,\n  \"saffron\":2,\n  \"sesame oil\":3,\n  \"cream of tartar\":2,\n  \"cinnamon\":8,\n  \"avocado\":6,\n  \"cheddar cheese\":45,\n  \"tomatoes\":69,\n  \"white pepper\":18,\n  \"brown rice\":2,\n  \"black beans\":10,\n  \"pinto beans\":10,\n  \"cauliflower\":2,\n  \"butter\":156,\n  \"frozen peas\":8,\n  \"salmon\":5,\n  \"orange juice\":7,\n  \"lemon\":18,\n  \"black pepper\":54,\n  \"black olives\":3,\n  \"buttermilk\":5,\n  \"powdered sugar\":2,\n  \"eggplant\":4,\n  \"dried oregano\":9,\n  \"capers\":5,\n  \"cornmeal\":8,\n  \"cloves\":13,\n  \"oil\":51,\n  \"chicken\":14,\n  \"blue cheese\":6,\n  \"cucumber\":5,\n  \"canola oil\":4,\n  \"flour tortilla\":4,\n  \"dried tarragon\":4,\n  \"celery\":59,\n  \"cornish game hens\":2,\n  \"alfalfa sprouts\":3,\n  \"fresh cilantro\":5,\n  \"nutmeg\":27,\n  \"walnuts\":2,\n  \"baking soda\":8,\n  \"bread crumbs\":18,\n  \"tabasco sauce\":16,\n  \"balsamic vinegar\":9,\n  \"ginger\":6,\n  \"fresh rosemary\":4,\n  \"lime\":2,\n  \"frozen corn\":2,\n  \"cumin\":16,\n  \"green onions\":45,\n  \"skim milk\":5,\n  \"barley\":4,\n  \"kidney beans\":18,\n  \"shortening\":5,\n  \"parmesan cheese\":42,\n  \"applesauce\":2,\n  \"carrots\":43,\n  \"mushrooms\":54,\n  \"sour cream\":76,\n  \"brussels sprouts\":3,\n  \"cream cheese\":14,\n  \"cherry tomatoes\":6,\n  \"salsa\":4,\n  \"parsley\":75,\n  \"green olives\":2,\n  \"molasses\":23,\n  \"beer\":14,\n  \"chili powder\":26,\n  \"spaghetti\":5,\n  \"raisins\":5,\n  \"red wine vinegar\":18,\n  \"chives\":18,\n  \"cornstarch\":33,\n  \"orange\":3,\n  \"dried basil\":10,\n  \"radishes\":2,\n  \"catsup\":32,\n  \"ground beef\":34,\n  \"romaine lettuce\":4,\n  \"mozzarella cheese\":3,\n  \"sweet red pepper\":4,\n  \"barbecue sauce\":6,\n  \"raw spinach\":2,\n  \"red pepper flakes\":7,\n  \"almonds\":4,\n  \"eggs\":157,\n  \"margarine\":13,\n  \"kale\":3,\n  \"garlic powder\":35,\n  \"dried thyme\":20,\n  \"cream of mushroom soup\":18,\n  \"whole wheat flour\":2,\n  \"salt\":516,\n  \"fresh basil\":4,\n  \"bean sprouts\":2,\n  \"potatoes\":75,\n  \"frozen spinach\":2,\n  \"celery seeds\":3,\n  \"allspice\":5,\n  \"dijon mustard\":21,\n  \"soy sauce\":36,\n  \"broccoli\":8,\n  \"apples\":7,\n  \"white rice\":2,\n  \"sweet potatoes\":2,\n  \"maple syrup\":5,\n  \"yellow mustard\":2,\n  \"egg whites\":7,\n  \"worcestershire sauce\":65,\n  \"dried cilantro\":2,\n  \"brown sugar\":83,\n  \"lime juice\":4,\n  \"dry mustard\":51,\n  \"mayonnaise\":54,\n  \"chicken wings\":2,\n  \"vegetable oil\":54,\n  \"olive oil\":59,\n  \"fresh thyme\":6,\n  \"curry powder\":6,\n  \"flour\":143,\n  \"vinegar\":97,\n  \"white wine\":27,\n  \"sherry\":11,\n  \"whipping cream\":16,\n  \"chicken breast\":2,\n  \"sugar\":128},\n \"baking soda\":\n {\"coconut oil\":2,\n  \"bacon\":8,\n  \"cayenne pepper\":9,\n  \"lemon juice\":110,\n  \"honey\":230,\n  \"swiss cheese\":2,\n  \"paprika\":6,\n  \"bread\":2,\n  \"ground cinnamon\":400,\n  \"yeast\":58,\n  \"egg yolks\":36,\n  \"green pepper\":6,\n  \"baking powder\":1877,\n  \"sesame oil\":8,\n  \"cinnamon\":991,\n  \"cream of tartar\":142,\n  \"pine nuts\":10,\n  \"cheddar cheese\":11,\n  \"tomatoes\":4,\n  \"white pepper\":9,\n  \"brown rice\":2,\n  \"black beans\":2,\n  \"pinto beans\":2,\n  \"butter\":1047,\n  \"frozen peas\":3,\n  \"orange juice\":137,\n  \"black pepper\":22,\n  \"lemon\":19,\n  \"tumeric\":3,\n  \"buttermilk\":635,\n  \"powdered sugar\":149,\n  \"dried oregano\":5,\n  \"eggplant\":3,\n  \"cornmeal\":93,\n  \"cloves\":209,\n  \"oil\":274,\n  \"chicken\":2,\n  \"cucumber\":2,\n  \"canola oil\":56,\n  \"strawberries\":19,\n  \"celery\":2,\n  \"fresh peaches\":4,\n  \"walnuts\":238,\n  \"nutmeg\":404,\n  \"fresh cilantro\":2,\n  \"bread crumbs\":6,\n  \"corn syrup\":50,\n  \"ginger\":175,\n  \"fresh rosemary\":2,\n  \"pecans\":133,\n  \"peanut butter\":115,\n  \"cumin\":6,\n  \"green onions\":5,\n  \"canned chicken broth\":2,\n  \"banana\":36,\n  \"skim milk\":95,\n  \"beets\":3,\n  \"shortening\":481,\n  \"parmesan cheese\":5,\n  \"applesauce\":145,\n  \"carrots\":55,\n  \"mushrooms\":3,\n  \"sour cream\":232,\n  \"yogurt\":27,\n  \"tofu\":4,\n  \"cream cheese\":69,\n  \"salsa\":2,\n  \"parsley\":4,\n  \"rice vinegar\":2,\n  \"molasses\":269,\n  \"beer\":10,\n  \"chili powder\":12,\n  \"raisins\":529,\n  \"garam masala\":2,\n  \"chives\":4,\n  \"cornstarch\":61,\n  \"orange\":41,\n  \"dried basil\":6,\n  \"catsup\":4,\n  \"ground beef\":2,\n  \"mozzarella cheese\":3,\n  \"sweet red pepper\":2,\n  \"cumin seeds\":4,\n  \"barbecue sauce\":2,\n  \"red pepper flakes\":2,\n  \"almonds\":40,\n  \"eggs\":1900,\n  \"margarine\":349,\n  \"fresh ginger\":6,\n  \"garlic powder\":6,\n  \"dried thyme\":7,\n  \"low fat milk\":3,\n  \"vanilla\":1393,\n  \"whole wheat flour\":248,\n  \"salt\":3399,\n  \"fresh basil\":3,\n  \"bean sprouts\":2,\n  \"potatoes\":5,\n  \"almond extract\":106,\n  \"allspice\":139,\n  \"soy sauce\":10,\n  \"apples\":83,\n  \"maple syrup\":47,\n  \"sweet potatoes\":5,\n  \"egg whites\":253,\n  \"worcestershire sauce\":3,\n  \"brown sugar\":903,\n  \"dry mustard\":7,\n  \"white flour\":48,\n  \"blueberries\":51,\n  \"mayonnaise\":10,\n  \"vegetable oil\":406,\n  \"olive oil\":17,\n  \"flour\":1969,\n  \"curry powder\":2,\n  \"vinegar\":74,\n  \"white wine\":2,\n  \"sherry\":5,\n  \"oats\":84,\n  \"whipping cream\":23,\n  \"sugar\":2741},\n \"black pepper\":\n {\"elbow macaroni\":9,\n  \"red wine\":44,\n  \"canned tomato sauce\":6,\n  \"bacon\":54,\n  \"cayenne pepper\":250,\n  \"lemon juice\":258,\n  \"honey\":72,\n  \"swiss cheese\":11,\n  \"zucchini squash\":2,\n  \"paprika\":328,\n  \"ground cinnamon\":30,\n  \"bread\":12,\n  \"dried bay leaf\":2,\n  \"yeast\":17,\n  \"egg yolks\":21,\n  \"green pepper\":90,\n  \"baking powder\":44,\n  \"saffron\":17,\n  \"sesame oil\":46,\n  \"cream of tartar\":3,\n  \"pine nuts\":24,\n  \"cinnamon\":87,\n  \"avocado\":7,\n  \"cheddar cheese\":45,\n  \"tomatoes\":187,\n  \"white pepper\":134,\n  \"brown rice\":11,\n  \"black beans\":21,\n  \"pinto beans\":24,\n  \"fresh garlic\":14,\n  \"cauliflower\":9,\n  \"frozen peas\":18,\n  \"butter\":341,\n  \"salmon\":4,\n  \"orange juice\":29,\n  \"lemon\":54,\n  \"tumeric\":14,\n  \"black olives\":15,\n  \"buttermilk\":18,\n  \"powdered sugar\":4,\n  \"hoisin sauce\":6,\n  \"dried oregano\":105,\n  \"eggplant\":41,\n  \"capers\":23,\n  \"cornmeal\":28,\n  \"cloves\":44,\n  \"oil\":163,\n  \"chicken\":61,\n  \"blue cheese\":9,\n  \"cucumber\":21,\n  \"dry rosemary\":2,\n  \"canola oil\":24,\n  \"dried tarragon\":13,\n  \"strawberries\":2,\n  \"celery\":134,\n  \"cornish game hens\":4,\n  \"walnuts\":16,\n  \"nutmeg\":73,\n  \"fresh cilantro\":14,\n  \"bread crumbs\":65,\n  \"baking soda\":22,\n  \"tabasco sauce\":78,\n  \"ginger\":74,\n  \"balsamic vinegar\":45,\n  \"fresh rosemary\":5,\n  \"lime\":14,\n  \"pecans\":14,\n  \"peanut butter\":8,\n  \"frozen corn\":12,\n  \"cumin\":171,\n  \"green onions\":113,\n  \"canned chicken broth\":2,\n  \"beets\":3,\n  \"banana\":4,\n  \"skim milk\":40,\n  \"barley\":3,\n  \"kidney beans\":21,\n  \"shortening\":17,\n  \"applesauce\":3,\n  \"parmesan cheese\":102,\n  \"carrots\":150,\n  \"mushrooms\":108,\n  \"sour cream\":80,\n  \"yogurt\":10,\n  \"cream cheese\":24,\n  \"tofu\":21,\n  \"cherry tomatoes\":11,\n  \"salsa\":9,\n  \"parsley\":207,\n  \"green olives\":9,\n  \"rice vinegar\":9,\n  \"molasses\":32,\n  \"beer\":22,\n  \"chili powder\":229,\n  \"spaghetti\":15,\n  \"raisins\":23,\n  \"red wine vinegar\":79,\n  \"garam masala\":13,\n  \"chives\":19,\n  \"cornstarch\":86,\n  \"orange\":10,\n  \"dried basil\":69,\n  \"radishes\":2,\n  \"catsup\":42,\n  \"ground beef\":74,\n  \"romaine lettuce\":4,\n  \"mozzarella cheese\":41,\n  \"sweet red pepper\":14,\n  \"canned tuna\":2,\n  \"cumin seeds\":22,\n  \"barbecue sauce\":8,\n  \"red pepper flakes\":35,\n  \"almonds\":13,\n  \"eggs\":176,\n  \"margarine\":91,\n  \"fresh ginger\":11,\n  \"kale\":4,\n  \"garlic powder\":273,\n  \"dried thyme\":112,\n  \"cream of mushroom soup\":13,\n  \"vanilla\":9,\n  \"whole wheat flour\":13,\n  \"salt\":1964,\n  \"fresh basil\":28,\n  \"bean sprouts\":10,\n  \"shiitake mushrooms\":7,\n  \"potatoes\":118,\n  \"almond extract\":2,\n  \"frozen spinach\":8,\n  \"celery seeds\":10,\n  \"allspice\":68,\n  \"dijon mustard\":76,\n  \"soy sauce\":173,\n  \"broccoli\":13,\n  \"chicken legs\":3,\n  \"apples\":15,\n  \"sweet potatoes\":8,\n  \"maple syrup\":12,\n  \"white rice\":11,\n  \"yellow mustard\":10,\n  \"red leaf lettuce\":2,\n  \"egg whites\":37,\n  \"worcestershire sauce\":183,\n  \"dried cilantro\":3,\n  \"brown sugar\":145,\n  \"lime juice\":36,\n  \"dry mustard\":133,\n  \"raw shrimp\":3,\n  \"white flour\":8,\n  \"mayonnaise\":59,\n  \"chicken wings\":17,\n  \"vegetable oil\":202,\n  \"fresh thyme\":13,\n  \"olive oil\":569,\n  \"flour\":266,\n  \"curry powder\":64,\n  \"vinegar\":121,\n  \"white wine\":58,\n  \"sherry\":20,\n  \"yam\":3,\n  \"whipping cream\":9,\n  \"chicken breast\":13,\n  \"sugar\":297},\n \"brown rice\":\n {\"canned tomato sauce\":2,\n  \"bacon\":2,\n  \"cayenne pepper\":5,\n  \"lemon juice\":12,\n  \"honey\":12,\n  \"zucchini squash\":2,\n  \"swiss cheese\":3,\n  \"paprika\":14,\n  \"ground cinnamon\":7,\n  \"egg yolks\":2,\n  \"green pepper\":13,\n  \"baking powder\":2,\n  \"saffron\":2,\n  \"sesame oil\":4,\n  \"pine nuts\":3,\n  \"cinnamon\":15,\n  \"avocado\":2,\n  \"cheddar cheese\":4,\n  \"tomatoes\":28,\n  \"white pepper\":7,\n  \"black beans\":7,\n  \"pinto beans\":2,\n  \"cauliflower\":2,\n  \"butter\":20,\n  \"frozen peas\":4,\n  \"salmon\":2,\n  \"orange juice\":2,\n  \"lemon\":2,\n  \"black pepper\":11,\n  \"black olives\":2,\n  \"buttermilk\":2,\n  \"eggplant\":4,\n  \"dried oregano\":6,\n  \"cornmeal\":2,\n  \"cloves\":5,\n  \"oil\":16,\n  \"chicken\":3,\n  \"cucumber\":3,\n  \"canola oil\":2,\n  \"dried tarragon\":3,\n  \"celery\":27,\n  \"alfalfa sprouts\":2,\n  \"nutmeg\":5,\n  \"walnuts\":2,\n  \"baking soda\":2,\n  \"bread crumbs\":7,\n  \"tabasco sauce\":12,\n  \"ginger\":3,\n  \"balsamic vinegar\":3,\n  \"lime\":2,\n  \"pecans\":3,\n  \"peanut butter\":7,\n  \"green onions\":13,\n  \"cumin\":17,\n  \"canned chicken broth\":2,\n  \"skim milk\":3,\n  \"barley\":9,\n  \"kidney beans\":7,\n  \"parmesan cheese\":4,\n  \"carrots\":24,\n  \"mushrooms\":19,\n  \"sour cream\":3,\n  \"tofu\":6,\n  \"salsa\":4,\n  \"parsley\":21,\n  \"green olives\":2,\n  \"rice vinegar\":4,\n  \"molasses\":5,\n  \"chili powder\":18,\n  \"raisins\":7,\n  \"red wine vinegar\":4,\n  \"chives\":2,\n  \"cornstarch\":5,\n  \"dried basil\":11,\n  \"orange\":4,\n  \"ground beef\":4,\n  \"adzuki beans\":2,\n  \"mozzarella cheese\":3,\n  \"sweet red pepper\":4,\n  \"cumin seeds\":3,\n  \"red pepper flakes\":2,\n  \"eggs\":12,\n  \"almonds\":3,\n  \"margarine\":5,\n  \"fresh ginger\":2,\n  \"garlic powder\":20,\n  \"dried thyme\":6,\n  \"vanilla\":3,\n  \"whole wheat flour\":3,\n  \"salt\":75,\n  \"bean sprouts\":4,\n  \"potatoes\":4,\n  \"frozen spinach\":2,\n  \"allspice\":2,\n  \"dijon mustard\":2,\n  \"soy sauce\":24,\n  \"broccoli\":5,\n  \"apples\":3,\n  \"white rice\":3,\n  \"maple syrup\":2,\n  \"egg whites\":2,\n  \"worcestershire sauce\":2,\n  \"dry mustard\":3,\n  \"mayonnaise\":2,\n  \"fresh thyme\":2,\n  \"vegetable oil\":27,\n  \"olive oil\":27,\n  \"flour\":4,\n  \"curry powder\":13,\n  \"vinegar\":2,\n  \"white wine\":4,\n  \"sherry\":2,\n  \"sugar\":6},\n \"baking powder\":\n {\"red wine\":3,\n  \"bacon\":21,\n  \"cayenne pepper\":20,\n  \"lemon juice\":206,\n  \"honey\":305,\n  \"zucchini squash\":2,\n  \"swiss cheese\":4,\n  \"paprika\":19,\n  \"ground cinnamon\":392,\n  \"bread\":9,\n  \"yeast\":21,\n  \"egg yolks\":152,\n  \"green pepper\":11,\n  \"saffron\":3,\n  \"sesame oil\":7,\n  \"cream of tartar\":78,\n  \"cinnamon\":1027,\n  \"pine nuts\":21,\n  \"cheddar cheese\":42,\n  \"tomatoes\":5,\n  \"white pepper\":14,\n  \"brown rice\":2,\n  \"black beans\":4,\n  \"fresh garlic\":2,\n  \"pinto beans\":2,\n  \"cauliflower\":2,\n  \"frozen peas\":4,\n  \"butter\":1518,\n  \"orange juice\":211,\n  \"lemon\":55,\n  \"black pepper\":44,\n  \"tumeric\":3,\n  \"black olives\":5,\n  \"buttermilk\":425,\n  \"powdered sugar\":219,\n  \"eggplant\":6,\n  \"dried oregano\":11,\n  \"capers\":2,\n  \"cornmeal\":161,\n  \"cloves\":179,\n  \"oil\":405,\n  \"chicken\":13,\n  \"blue cheese\":2,\n  \"canola oil\":79,\n  \"dried tarragon\":3,\n  \"strawberries\":41,\n  \"celery\":12,\n  \"walnuts\":237,\n  \"nutmeg\":423,\n  \"fresh peaches\":12,\n  \"fresh cilantro\":4,\n  \"baking soda\":1877,\n  \"bread crumbs\":9,\n  \"tabasco sauce\":6,\n  \"corn syrup\":26,\n  \"ginger\":95,\n  \"pecans\":138,\n  \"peanut butter\":80,\n  \"frozen corn\":4,\n  \"green onions\":16,\n  \"cumin\":10,\n  \"beets\":2,\n  \"banana\":37,\n  \"skim milk\":218,\n  \"barley\":2,\n  \"kidney beans\":2,\n  \"shortening\":675,\n  \"parmesan cheese\":12,\n  \"applesauce\":167,\n  \"carrots\":70,\n  \"mushrooms\":10,\n  \"sour cream\":188,\n  \"yogurt\":32,\n  \"cream cheese\":83,\n  \"tofu\":5,\n  \"salsa\":6,\n  \"parsley\":21,\n  \"molasses\":135,\n  \"chili powder\":23,\n  \"beer\":31,\n  \"raisins\":483,\n  \"garam masala\":4,\n  \"chives\":10,\n  \"cornstarch\":132,\n  \"orange\":45,\n  \"dried basil\":10,\n  \"catsup\":3,\n  \"ground beef\":8,\n  \"mozzarella cheese\":6,\n  \"sweet red pepper\":6,\n  \"cumin seeds\":2,\n  \"canned peaches\":2,\n  \"red pepper flakes\":2,\n  \"almonds\":93,\n  \"eggs\":2558,\n  \"margarine\":372,\n  \"fresh ginger\":3,\n  \"kale\":2,\n  \"garlic powder\":22,\n  \"dried thyme\":16,\n  \"low fat milk\":2,\n  \"vanilla\":1555,\n  \"whole wheat flour\":282,\n  \"salt\":4656,\n  \"fresh basil\":5,\n  \"bean sprouts\":2,\n  \"potatoes\":18,\n  \"almond extract\":182,\n  \"canned apricots\":2,\n  \"celery seeds\":2,\n  \"allspice\":124,\n  \"dijon mustard\":3,\n  \"soy sauce\":13,\n  \"broccoli\":3,\n  \"apples\":115,\n  \"maple syrup\":76,\n  \"sweet potatoes\":11,\n  \"egg whites\":442,\n  \"worcestershire sauce\":8,\n  \"brown sugar\":740,\n  \"lime juice\":14,\n  \"dry mustard\":16,\n  \"white flour\":44,\n  \"blueberries\":118,\n  \"mayonnaise\":14,\n  \"chicken wings\":2,\n  \"fresh thyme\":2,\n  \"vegetable oil\":527,\n  \"olive oil\":38,\n  \"flour\":3076,\n  \"curry powder\":12,\n  \"vinegar\":23,\n  \"white wine\":7,\n  \"sherry\":7,\n  \"oats\":43,\n  \"whipping cream\":68,\n  \"chicken breast\":3,\n  \"sugar\":3957},\n \"avocado\":\n {\"sour cream\":29,\n  \"yogurt\":2,\n  \"red wine\":2,\n  \"cream cheese\":8,\n  \"cherry tomatoes\":4,\n  \"bacon\":6,\n  \"salsa\":8,\n  \"parsley\":6,\n  \"cayenne pepper\":6,\n  \"green olives\":3,\n  \"rice vinegar\":2,\n  \"honey\":4,\n  \"lemon juice\":34,\n  \"swiss cheese\":3,\n  \"beer\":2,\n  \"paprika\":6,\n  \"chili powder\":20,\n  \"red wine vinegar\":8,\n  \"chives\":3,\n  \"green pepper\":2,\n  \"cornstarch\":3,\n  \"dried basil\":2,\n  \"orange\":4,\n  \"sesame oil\":2,\n  \"radishes\":3,\n  \"catsup\":4,\n  \"cinnamon\":3,\n  \"ground beef\":8,\n  \"cheddar cheese\":16,\n  \"romaine lettuce\":2,\n  \"corn tortilla\":2,\n  \"sweet red pepper\":2,\n  \"tomatoes\":38,\n  \"cumin seeds\":2,\n  \"white pepper\":5,\n  \"brown rice\":2,\n  \"red pepper flakes\":2,\n  \"almonds\":2,\n  \"eggs\":10,\n  \"black beans\":5,\n  \"frozen peas\":2,\n  \"butter\":14,\n  \"garlic powder\":5,\n  \"orange juice\":2,\n  \"black pepper\":7,\n  \"lemon\":8,\n  \"salt\":71,\n  \"bean sprouts\":2,\n  \"black olives\":4,\n  \"buttermilk\":2,\n  \"eggplant\":2,\n  \"dried oregano\":12,\n  \"capers\":4,\n  \"cornmeal\":2,\n  \"cloves\":3,\n  \"oil\":11,\n  \"chicken\":4,\n  \"blue cheese\":3,\n  \"cucumber\":9,\n  \"canola oil\":4,\n  \"dijon mustard\":3,\n  \"soy sauce\":3,\n  \"strawberries\":4,\n  \"celery\":2,\n  \"broccoli\":2,\n  \"shell macaroni\":2,\n  \"white rice\":2,\n  \"alfalfa sprouts\":6,\n  \"fresh cilantro\":11,\n  \"red leaf lettuce\":2,\n  \"bread crumbs\":2,\n  \"worcestershire sauce\":8,\n  \"tabasco sauce\":3,\n  \"balsamic vinegar\":2,\n  \"lime juice\":26,\n  \"dry mustard\":2,\n  \"lime\":5,\n  \"mayonnaise\":19,\n  \"green onions\":17,\n  \"cumin\":11,\n  \"olive oil\":37,\n  \"vegetable oil\":13,\n  \"curry powder\":2,\n  \"flour\":7,\n  \"kidney beans\":7,\n  \"vinegar\":10,\n  \"white wine\":3,\n  \"parmesan cheese\":5,\n  \"carrots\":2,\n  \"sugar\":16,\n  \"mushrooms\":3},\n \"saffron\":\n {\"sour cream\":5,\n  \"yogurt\":7,\n  \"red wine\":2,\n  \"cherry tomatoes\":2,\n  \"bacon\":2,\n  \"parsley\":18,\n  \"cayenne pepper\":4,\n  \"green olives\":4,\n  \"lemon juice\":12,\n  \"honey\":5,\n  \"beer\":3,\n  \"chili powder\":3,\n  \"paprika\":18,\n  \"bread\":2,\n  \"ground cinnamon\":2,\n  \"raisins\":4,\n  \"red wine vinegar\":2,\n  \"yeast\":3,\n  \"chives\":2,\n  \"garam masala\":8,\n  \"egg yolks\":4,\n  \"green pepper\":16,\n  \"baking powder\":3,\n  \"cornstarch\":7,\n  \"dried basil\":2,\n  \"cinnamon\":13,\n  \"pine nuts\":4,\n  \"sweet red pepper\":4,\n  \"tomatoes\":26,\n  \"cumin seeds\":5,\n  \"white pepper\":8,\n  \"brown rice\":2,\n  \"red pepper flakes\":2,\n  \"eggs\":8,\n  \"almonds\":11,\n  \"margarine\":5,\n  \"fresh ginger\":4,\n  \"frozen peas\":3,\n  \"butter\":35,\n  \"garlic powder\":4,\n  \"dried thyme\":3,\n  \"salt\":83,\n  \"black pepper\":17,\n  \"lemon\":7,\n  \"fresh basil\":2,\n  \"tumeric\":3,\n  \"black olives\":2,\n  \"potatoes\":5,\n  \"almond extract\":2,\n  \"capers\":6,\n  \"cornmeal\":2,\n  \"cloves\":12,\n  \"oil\":16,\n  \"chicken\":23,\n  \"cucumber\":2,\n  \"allspice\":2,\n  \"canola oil\":2,\n  \"soy sauce\":2,\n  \"celery\":4,\n  \"white rice\":2,\n  \"nutmeg\":8,\n  \"walnuts\":3,\n  \"fresh cilantro\":2,\n  \"tabasco sauce\":4,\n  \"brown sugar\":3,\n  \"ginger\":13,\n  \"lime juice\":2,\n  \"dry mustard\":3,\n  \"raw shrimp\":3,\n  \"mayonnaise\":2,\n  \"green onions\":4,\n  \"cumin\":3,\n  \"canned chicken broth\":2,\n  \"olive oil\":68,\n  \"vegetable oil\":8,\n  \"fresh thyme\":2,\n  \"curry powder\":2,\n  \"flour\":10,\n  \"shortening\":2,\n  \"vinegar\":7,\n  \"parmesan cheese\":6,\n  \"white wine\":9,\n  \"carrots\":6,\n  \"whipping cream\":5,\n  \"chicken breast\":2,\n  \"sugar\":17,\n  \"mushrooms\":4},\n \"whole wheat flour\":\n {\"bacon\":2,\n  \"cayenne pepper\":8,\n  \"lemon juice\":29,\n  \"honey\":246,\n  \"zucchini squash\":2,\n  \"swiss cheese\":2,\n  \"paprika\":4,\n  \"ground cinnamon\":54,\n  \"bread\":2,\n  \"yeast\":221,\n  \"egg yolks\":2,\n  \"green pepper\":3,\n  \"baking powder\":282,\n  \"sesame oil\":2,\n  \"cinnamon\":139,\n  \"cream of tartar\":8,\n  \"pine nuts\":4,\n  \"cheddar cheese\":6,\n  \"tomatoes\":2,\n  \"white pepper\":4,\n  \"brown rice\":3,\n  \"black beans\":2,\n  \"frozen peas\":3,\n  \"butter\":131,\n  \"orange juice\":30,\n  \"black pepper\":13,\n  \"black olives\":3,\n  \"buttermilk\":75,\n  \"powdered sugar\":5,\n  \"dried oregano\":7,\n  \"eggplant\":2,\n  \"cornmeal\":57,\n  \"cloves\":16,\n  \"oil\":89,\n  \"chicken\":2,\n  \"cucumber\":4,\n  \"canola oil\":37,\n  \"dried tarragon\":2,\n  \"strawberries\":2,\n  \"fresh cilantro\":2,\n  \"fresh peaches\":2,\n  \"nutmeg\":47,\n  \"walnuts\":39,\n  \"baking soda\":248,\n  \"bread crumbs\":2,\n  \"corn syrup\":5,\n  \"tabasco sauce\":2,\n  \"ginger\":22,\n  \"fresh rosemary\":2,\n  \"pecans\":15,\n  \"peanut butter\":11,\n  \"cumin\":2,\n  \"frozen corn\":2,\n  \"skim milk\":48,\n  \"banana\":18,\n  \"shortening\":28,\n  \"applesauce\":56,\n  \"parmesan cheese\":7,\n  \"carrots\":21,\n  \"mushrooms\":8,\n  \"sour cream\":15,\n  \"yogurt\":7,\n  \"tofu\":3,\n  \"cream cheese\":5,\n  \"parsley\":7,\n  \"molasses\":111,\n  \"chili powder\":6,\n  \"beer\":5,\n  \"raisins\":111,\n  \"chives\":6,\n  \"garam masala\":3,\n  \"cornstarch\":7,\n  \"orange\":7,\n  \"dried basil\":9,\n  \"catsup\":2,\n  \"ground beef\":2,\n  \"mozzarella cheese\":4,\n  \"almonds\":6,\n  \"eggs\":135,\n  \"margarine\":63,\n  \"garlic powder\":28,\n  \"dried thyme\":6,\n  \"low fat milk\":2,\n  \"vanilla\":78,\n  \"salt\":756,\n  \"fresh basil\":4,\n  \"potatoes\":11,\n  \"almond extract\":13,\n  \"frozen spinach\":2,\n  \"allspice\":21,\n  \"celery seeds\":2,\n  \"dijon mustard\":5,\n  \"soy sauce\":15,\n  \"apples\":16,\n  \"sweet potatoes\":3,\n  \"maple syrup\":17,\n  \"egg whites\":51,\n  \"worcestershire sauce\":2,\n  \"brown sugar\":136,\n  \"dry mustard\":2,\n  \"white flour\":70,\n  \"blueberries\":11,\n  \"fresh thyme\":2,\n  \"olive oil\":58,\n  \"vegetable oil\":139,\n  \"flour\":76,\n  \"curry powder\":4,\n  \"vinegar\":2,\n  \"white wine\":2,\n  \"sherry\":2,\n  \"oats\":18,\n  \"whipping cream\":2,\n  \"sugar\":302},\n \"mayonnaise\":\n {\"elbow macaroni\":4,\n  \"bacon\":54,\n  \"cayenne pepper\":40,\n  \"honey\":36,\n  \"lemon juice\":333,\n  \"swiss cheese\":22,\n  \"paprika\":97,\n  \"camembert cheese\":2,\n  \"ground cinnamon\":5,\n  \"bread\":21,\n  \"egg yolks\":7,\n  \"green pepper\":62,\n  \"baking powder\":14,\n  \"saffron\":2,\n  \"sesame oil\":6,\n  \"pine nuts\":5,\n  \"cinnamon\":10,\n  \"avocado\":19,\n  \"cheddar cheese\":56,\n  \"tomatoes\":52,\n  \"white pepper\":33,\n  \"brown rice\":2,\n  \"cauliflower\":4,\n  \"frozen peas\":10,\n  \"butter\":103,\n  \"salmon\":9,\n  \"orange juice\":9,\n  \"lemon\":44,\n  \"black pepper\":59,\n  \"tumeric\":2,\n  \"black olives\":13,\n  \"buttermilk\":21,\n  \"powdered sugar\":7,\n  \"eggplant\":4,\n  \"hoisin sauce\":2,\n  \"dried oregano\":15,\n  \"capers\":32,\n  \"cornmeal\":3,\n  \"cloves\":7,\n  \"oil\":31,\n  \"chicken\":38,\n  \"blue cheese\":16,\n  \"cucumber\":37,\n  \"flour tortilla\":2,\n  \"dried tarragon\":12,\n  \"strawberries\":2,\n  \"celery\":127,\n  \"alfalfa sprouts\":3,\n  \"fresh cilantro\":8,\n  \"nutmeg\":5,\n  \"walnuts\":21,\n  \"baking soda\":10,\n  \"bread crumbs\":33,\n  \"tabasco sauce\":43,\n  \"balsamic vinegar\":3,\n  \"ginger\":6,\n  \"lime\":5,\n  \"pecans\":24,\n  \"peanut butter\":4,\n  \"frozen corn\":2,\n  \"cumin\":5,\n  \"green onions\":107,\n  \"beets\":3,\n  \"skim milk\":4,\n  \"banana\":2,\n  \"kidney beans\":2,\n  \"shortening\":3,\n  \"parmesan cheese\":75,\n  \"carrots\":32,\n  \"mushrooms\":16,\n  \"sour cream\":235,\n  \"yogurt\":15,\n  \"tofu\":3,\n  \"cream cheese\":88,\n  \"cherry tomatoes\":7,\n  \"salsa\":6,\n  \"parsley\":72,\n  \"green olives\":8,\n  \"rice vinegar\":4,\n  \"beer\":8,\n  \"chili powder\":17,\n  \"raisins\":20,\n  \"red wine vinegar\":21,\n  \"garam masala\":2,\n  \"chives\":19,\n  \"cornstarch\":8,\n  \"orange\":3,\n  \"dried basil\":13,\n  \"catsup\":25,\n  \"radishes\":9,\n  \"ground beef\":14,\n  \"romaine lettuce\":3,\n  \"mozzarella cheese\":14,\n  \"sweet red pepper\":5,\n  \"canned tuna\":3,\n  \"barbecue sauce\":3,\n  \"red pepper flakes\":3,\n  \"eggs\":145,\n  \"almonds\":11,\n  \"margarine\":13,\n  \"fresh ginger\":2,\n  \"garlic powder\":86,\n  \"dried thyme\":8,\n  \"cream of mushroom soup\":25,\n  \"vanilla\":10,\n  \"salt\":493,\n  \"fresh basil\":7,\n  \"bean sprouts\":2,\n  \"potatoes\":24,\n  \"almond extract\":2,\n  \"frozen spinach\":7,\n  \"allspice\":5,\n  \"celery seeds\":5,\n  \"dijon mustard\":98,\n  \"soy sauce\":20,\n  \"broccoli\":14,\n  \"apples\":16,\n  \"maple syrup\":2,\n  \"shell macaroni\":2,\n  \"sweet potatoes\":2,\n  \"yellow mustard\":15,\n  \"red leaf lettuce\":2,\n  \"egg whites\":6,\n  \"worcestershire sauce\":164,\n  \"brown sugar\":10,\n  \"lime juice\":17,\n  \"dry mustard\":77,\n  \"raw shrimp\":3,\n  \"chicken wings\":5,\n  \"fresh thyme\":2,\n  \"olive oil\":52,\n  \"vegetable oil\":24,\n  \"curry powder\":98,\n  \"flour\":61,\n  \"vinegar\":74,\n  \"white wine\":4,\n  \"sherry\":7,\n  \"whipping cream\":25,\n  \"chicken breast\":6,\n  \"sugar\":159},\n \"almond extract\":\n {\"sour cream\":38,\n  \"yogurt\":2,\n  \"red wine\":2,\n  \"tofu\":2,\n  \"cream cheese\":43,\n  \"honey\":33,\n  \"lemon juice\":37,\n  \"molasses\":4,\n  \"ground cinnamon\":17,\n  \"raisins\":14,\n  \"yeast\":7,\n  \"egg yolks\":30,\n  \"cornstarch\":28,\n  \"baking powder\":182,\n  \"saffron\":2,\n  \"orange\":7,\n  \"pine nuts\":5,\n  \"cinnamon\":47,\n  \"cream of tartar\":39,\n  \"almonds\":74,\n  \"eggs\":230,\n  \"margarine\":55,\n  \"butter\":163,\n  \"low fat milk\":2,\n  \"vanilla\":161,\n  \"orange juice\":21,\n  \"whole wheat flour\":13,\n  \"black pepper\":2,\n  \"lemon\":5,\n  \"salt\":330,\n  \"buttermilk\":16,\n  \"powdered sugar\":61,\n  \"cornmeal\":2,\n  \"cloves\":7,\n  \"oil\":20,\n  \"canola oil\":5,\n  \"allspice\":3,\n  \"strawberries\":6,\n  \"apples\":6,\n  \"maple syrup\":5,\n  \"fresh peaches\":3,\n  \"walnuts\":15,\n  \"nutmeg\":16,\n  \"egg whites\":81,\n  \"baking soda\":106,\n  \"corn syrup\":6,\n  \"brown sugar\":50,\n  \"ginger\":3,\n  \"pecans\":10,\n  \"blueberries\":3,\n  \"peanut butter\":3,\n  \"mayonnaise\":2,\n  \"canned chicken broth\":2,\n  \"vegetable oil\":21,\n  \"skim milk\":18,\n  \"banana\":4,\n  \"flour\":227,\n  \"vinegar\":3,\n  \"shortening\":37,\n  \"white wine\":4,\n  \"applesauce\":5,\n  \"carrots\":2,\n  \"oats\":8,\n  \"whipping cream\":31,\n  \"sugar\":510},\n \"dried cilantro\":\n {\"sour cream\":2,\n  \"buttermilk\":2,\n  \"bacon\":2,\n  \"cayenne pepper\":2,\n  \"lemon juice\":3,\n  \"chicken\":2,\n  \"paprika\":3,\n  \"chili powder\":3,\n  \"allspice\":2,\n  \"canola oil\":2,\n  \"celery\":2,\n  \"green pepper\":2,\n  \"fresh cilantro\":3,\n  \"sesame oil\":2,\n  \"brown sugar\":2,\n  \"ground beef\":2,\n  \"ginger\":3,\n  \"lime juice\":4,\n  \"tomatoes\":2,\n  \"sweet red pepper\":2,\n  \"green onions\":2,\n  \"cumin\":5,\n  \"olive oil\":2,\n  \"white pepper\":2,\n  \"red pepper flakes\":2,\n  \"flour\":2,\n  \"butter\":2,\n  \"vinegar\":2,\n  \"garlic powder\":3,\n  \"carrots\":2,\n  \"sugar\":4,\n  \"black pepper\":3,\n  \"salt\":6},\n \"celery seeds\":\n {\"sour cream\":3,\n  \"elbow macaroni\":2,\n  \"cream cheese\":2,\n  \"bacon\":3,\n  \"cayenne pepper\":6,\n  \"lemon juice\":10,\n  \"honey\":5,\n  \"molasses\":2,\n  \"paprika\":14,\n  \"ground cinnamon\":2,\n  \"yeast\":3,\n  \"red wine vinegar\":6,\n  \"egg yolks\":2,\n  \"green pepper\":3,\n  \"cornstarch\":2,\n  \"baking powder\":2,\n  \"catsup\":2,\n  \"ground beef\":2,\n  \"cream of tartar\":2,\n  \"pine nuts\":2,\n  \"cinnamon\":3,\n  \"tomatoes\":4,\n  \"sweet red pepper\":4,\n  \"cumin seeds\":3,\n  \"white pepper\":5,\n  \"eggs\":2,\n  \"margarine\":2,\n  \"cauliflower\":3,\n  \"fresh ginger\":2,\n  \"butter\":6,\n  \"garlic powder\":6,\n  \"dried thyme\":5,\n  \"vanilla\":2,\n  \"whole wheat flour\":2,\n  \"black pepper\":10,\n  \"lemon\":3,\n  \"salt\":57,\n  \"fresh basil\":3,\n  \"tumeric\":5,\n  \"potatoes\":3,\n  \"cloves\":2,\n  \"oil\":2,\n  \"chicken\":2,\n  \"cucumber\":2,\n  \"allspice\":2,\n  \"dried tarragon\":2,\n  \"dijon mustard\":4,\n  \"soy sauce\":3,\n  \"celery\":5,\n  \"apples\":2,\n  \"nutmeg\":5,\n  \"egg whites\":2,\n  \"bread crumbs\":2,\n  \"worcestershire sauce\":5,\n  \"tabasco sauce\":2,\n  \"brown sugar\":8,\n  \"balsamic vinegar\":2,\n  \"ginger\":3,\n  \"lime juice\":2,\n  \"dry mustard\":17,\n  \"mayonnaise\":5,\n  \"green onions\":2,\n  \"cumin\":2,\n  \"chicken wings\":2,\n  \"olive oil\":11,\n  \"vegetable oil\":6,\n  \"beets\":2,\n  \"curry powder\":5,\n  \"flour\":3,\n  \"kidney beans\":2,\n  \"vinegar\":15,\n  \"carrots\":8,\n  \"whipping cream\":2,\n  \"sugar\":36,\n  \"mushrooms\":2},\n \"frozen spinach\":\n {\"sour cream\":7,\n  \"yogurt\":2,\n  \"tofu\":2,\n  \"cream cheese\":6,\n  \"bacon\":2,\n  \"salsa\":2,\n  \"parsley\":6,\n  \"cayenne pepper\":6,\n  \"lemon juice\":7,\n  \"swiss cheese\":4,\n  \"chili powder\":2,\n  \"paprika\":3,\n  \"ground cinnamon\":2,\n  \"raisins\":3,\n  \"garam masala\":3,\n  \"cornstarch\":2,\n  \"dried basil\":2,\n  \"ground beef\":4,\n  \"pine nuts\":2,\n  \"cheddar cheese\":7,\n  \"mozzarella cheese\":11,\n  \"tomatoes\":7,\n  \"cumin seeds\":2,\n  \"white pepper\":4,\n  \"brown rice\":2,\n  \"eggs\":22,\n  \"margarine\":6,\n  \"fresh ginger\":4,\n  \"cauliflower\":2,\n  \"frozen peas\":2,\n  \"butter\":27,\n  \"garlic powder\":6,\n  \"dried thyme\":3,\n  \"cream of mushroom soup\":2,\n  \"whole wheat flour\":2,\n  \"black pepper\":8,\n  \"salt\":38,\n  \"lemon\":3,\n  \"fresh basil\":2,\n  \"black olives\":2,\n  \"potatoes\":2,\n  \"buttermilk\":2,\n  \"dried oregano\":3,\n  \"eggplant\":2,\n  \"oil\":9,\n  \"celery\":2,\n  \"broccoli\":2,\n  \"nutmeg\":16,\n  \"egg whites\":2,\n  \"bread crumbs\":5,\n  \"worcestershire sauce\":2,\n  \"ginger\":3,\n  \"dry mustard\":2,\n  \"pecans\":3,\n  \"mayonnaise\":7,\n  \"green onions\":11,\n  \"cumin\":4,\n  \"vegetable oil\":7,\n  \"olive oil\":13,\n  \"skim milk\":3,\n  \"curry powder\":3,\n  \"flour\":18,\n  \"parmesan cheese\":13,\n  \"white wine\":2,\n  \"carrots\":3,\n  \"whipping cream\":2,\n  \"mushrooms\":6,\n  \"sugar\":8},\n \"egg whites\":\n {\"red wine\":2,\n  \"bacon\":7,\n  \"cayenne pepper\":8,\n  \"honey\":71,\n  \"lemon juice\":151,\n  \"zucchini squash\":2,\n  \"swiss cheese\":5,\n  \"paprika\":15,\n  \"bread\":4,\n  \"ground cinnamon\":87,\n  \"yeast\":19,\n  \"egg yolks\":368,\n  \"green pepper\":5,\n  \"baking powder\":442,\n  \"sesame oil\":10,\n  \"pine nuts\":11,\n  \"cream of tartar\":254,\n  \"cinnamon\":167,\n  \"cheddar cheese\":10,\n  \"tomatoes\":7,\n  \"white pepper\":12,\n  \"brown rice\":2,\n  \"black beans\":3,\n  \"pinto beans\":2,\n  \"cauliflower\":2,\n  \"butter\":221,\n  \"salmon\":5,\n  \"orange juice\":59,\n  \"lemon\":31,\n  \"black pepper\":37,\n  \"buttermilk\":46,\n  \"powdered sugar\":124,\n  \"eggplant\":5,\n  \"dried oregano\":11,\n  \"cornmeal\":28,\n  \"cloves\":29,\n  \"oil\":44,\n  \"chicken\":3,\n  \"canola oil\":31,\n  \"strawberries\":29,\n  \"celery\":11,\n  \"fresh cilantro\":2,\n  \"walnuts\":36,\n  \"nutmeg\":77,\n  \"baking soda\":253,\n  \"bread crumbs\":22,\n  \"tabasco sauce\":2,\n  \"corn syrup\":35,\n  \"balsamic vinegar\":4,\n  \"ginger\":28,\n  \"lime\":4,\n  \"pecans\":32,\n  \"peanut butter\":13,\n  \"frozen corn\":2,\n  \"green onions\":18,\n  \"cumin\":4,\n  \"canned chicken broth\":2,\n  \"skim milk\":150,\n  \"banana\":7,\n  \"beets\":2,\n  \"barley\":2,\n  \"kidney beans\":2,\n  \"shortening\":35,\n  \"applesauce\":55,\n  \"parmesan cheese\":18,\n  \"carrots\":17,\n  \"mushrooms\":10,\n  \"sour cream\":23,\n  \"yogurt\":6,\n  \"tofu\":4,\n  \"cream cheese\":18,\n  \"cherry tomatoes\":2,\n  \"salsa\":5,\n  \"parsley\":14,\n  \"rice vinegar\":2,\n  \"molasses\":27,\n  \"beer\":3,\n  \"chili powder\":5,\n  \"spaghetti\":3,\n  \"raisins\":74,\n  \"red wine vinegar\":3,\n  \"chives\":6,\n  \"cornstarch\":147,\n  \"orange\":9,\n  \"dried basil\":11,\n  \"catsup\":3,\n  \"ground beef\":2,\n  \"mozzarella cheese\":12,\n  \"sweet red pepper\":2,\n  \"barbecue sauce\":2,\n  \"red pepper flakes\":4,\n  \"eggs\":105,\n  \"almonds\":36,\n  \"margarine\":107,\n  \"fresh ginger\":5,\n  \"garlic powder\":20,\n  \"dried thyme\":7,\n  \"vanilla\":517,\n  \"whole wheat flour\":51,\n  \"salt\":945,\n  \"fresh basil\":4,\n  \"bean sprouts\":3,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":11,\n  \"almond extract\":81,\n  \"frozen spinach\":2,\n  \"celery seeds\":2,\n  \"allspice\":13,\n  \"dijon mustard\":5,\n  \"soy sauce\":14,\n  \"chicken legs\":2,\n  \"apples\":28,\n  \"maple syrup\":36,\n  \"sweet potatoes\":6,\n  \"worcestershire sauce\":7,\n  \"brown sugar\":109,\n  \"lime juice\":9,\n  \"dry mustard\":11,\n  \"white flour\":4,\n  \"blueberries\":20,\n  \"mayonnaise\":6,\n  \"chicken wings\":3,\n  \"vegetable oil\":109,\n  \"olive oil\":44,\n  \"flour\":323,\n  \"curry powder\":5,\n  \"vinegar\":31,\n  \"white wine\":5,\n  \"sherry\":19,\n  \"oats\":13,\n  \"whipping cream\":92,\n  \"chicken breast\":6,\n  \"sugar\":1246},\n \"frozen french fries\":\n {\"worcestershire sauce\":2,\n  \"cheddar cheese\":2,\n  \"cream of mushroom soup\":3},\n \"molasses\":\n {\"red wine\":4,\n  \"canned tomato sauce\":3,\n  \"bacon\":23,\n  \"cayenne pepper\":15,\n  \"lemon juice\":42,\n  \"honey\":76,\n  \"paprika\":22,\n  \"bread\":3,\n  \"ground cinnamon\":77,\n  \"yeast\":99,\n  \"egg yolks\":5,\n  \"green pepper\":11,\n  \"baking powder\":135,\n  \"sesame oil\":2,\n  \"cinnamon\":221,\n  \"cream of tartar\":8,\n  \"tomatoes\":9,\n  \"white pepper\":5,\n  \"brown rice\":5,\n  \"black beans\":3,\n  \"pinto beans\":3,\n  \"butter\":181,\n  \"orange juice\":29,\n  \"lemon\":12,\n  \"black pepper\":32,\n  \"tumeric\":2,\n  \"buttermilk\":53,\n  \"powdered sugar\":14,\n  \"eggplant\":2,\n  \"dried oregano\":3,\n  \"hoisin sauce\":3,\n  \"capers\":2,\n  \"cornmeal\":40,\n  \"cloves\":78,\n  \"oil\":47,\n  \"chicken\":2,\n  \"canola oil\":13,\n  \"dried tarragon\":2,\n  \"celery\":8,\n  \"fresh peaches\":4,\n  \"nutmeg\":98,\n  \"walnuts\":9,\n  \"baking soda\":269,\n  \"bread crumbs\":3,\n  \"tabasco sauce\":19,\n  \"corn syrup\":5,\n  \"ginger\":162,\n  \"balsamic vinegar\":7,\n  \"fresh rosemary\":2,\n  \"lime\":2,\n  \"pecans\":9,\n  \"peanut butter\":7,\n  \"green onions\":4,\n  \"cumin\":11,\n  \"skim milk\":10,\n  \"banana\":2,\n  \"barley\":2,\n  \"kidney beans\":2,\n  \"shortening\":99,\n  \"applesauce\":21,\n  \"carrots\":8,\n  \"mushrooms\":6,\n  \"sour cream\":12,\n  \"yogurt\":3,\n  \"cream cheese\":3,\n  \"cherry tomatoes\":2,\n  \"parsley\":4,\n  \"rice vinegar\":3,\n  \"chili powder\":32,\n  \"beer\":14,\n  \"raisins\":97,\n  \"red wine vinegar\":20,\n  \"cornstarch\":21,\n  \"dried basil\":2,\n  \"orange\":9,\n  \"catsup\":31,\n  \"ground beef\":6,\n  \"cumin seeds\":3,\n  \"barbecue sauce\":3,\n  \"red pepper flakes\":4,\n  \"eggs\":125,\n  \"almonds\":8,\n  \"margarine\":38,\n  \"fresh ginger\":8,\n  \"garlic powder\":22,\n  \"dried thyme\":4,\n  \"vanilla\":47,\n  \"whole wheat flour\":111,\n  \"salt\":609,\n  \"bean sprouts\":2,\n  \"potatoes\":3,\n  \"almond extract\":4,\n  \"celery seeds\":2,\n  \"allspice\":51,\n  \"dijon mustard\":19,\n  \"soy sauce\":50,\n  \"apples\":9,\n  \"white rice\":2,\n  \"sweet potatoes\":3,\n  \"maple syrup\":15,\n  \"egg whites\":27,\n  \"worcestershire sauce\":48,\n  \"brown sugar\":194,\n  \"lime juice\":6,\n  \"dry mustard\":73,\n  \"white flour\":14,\n  \"blueberries\":9,\n  \"chicken wings\":5,\n  \"vegetable oil\":65,\n  \"olive oil\":31,\n  \"flour\":214,\n  \"curry powder\":9,\n  \"vinegar\":65,\n  \"white wine\":2,\n  \"sherry\":2,\n  \"oats\":12,\n  \"whipping cream\":3,\n  \"sugar\":256},\n \"sweet potatoes\":\n {\"red wine\":2,\n  \"bacon\":2,\n  \"cayenne pepper\":5,\n  \"lemon juice\":11,\n  \"honey\":16,\n  \"paprika\":3,\n  \"ground cinnamon\":21,\n  \"yeast\":3,\n  \"egg yolks\":5,\n  \"green pepper\":6,\n  \"baking powder\":11,\n  \"sesame oil\":2,\n  \"cream of tartar\":3,\n  \"cinnamon\":53,\n  \"cheddar cheese\":2,\n  \"tomatoes\":9,\n  \"white pepper\":4,\n  \"pinto beans\":2,\n  \"butter\":54,\n  \"frozen peas\":2,\n  \"orange juice\":28,\n  \"black pepper\":8,\n  \"lemon\":2,\n  \"buttermilk\":5,\n  \"eggplant\":2,\n  \"dried oregano\":2,\n  \"cloves\":5,\n  \"oil\":15,\n  \"chicken\":5,\n  \"cucumber\":2,\n  \"canola oil\":3,\n  \"celery\":5,\n  \"nutmeg\":36,\n  \"walnuts\":4,\n  \"baking soda\":5,\n  \"bread crumbs\":2,\n  \"corn syrup\":4,\n  \"tabasco sauce\":2,\n  \"balsamic vinegar\":5,\n  \"ginger\":11,\n  \"lime\":3,\n  \"pecans\":12,\n  \"peanut butter\":2,\n  \"green onions\":5,\n  \"cumin\":6,\n  \"canned chicken broth\":2,\n  \"skim milk\":5,\n  \"shortening\":5,\n  \"applesauce\":3,\n  \"carrots\":22,\n  \"mushrooms\":2,\n  \"sour cream\":2,\n  \"cream cheese\":4,\n  \"tofu\":2,\n  \"cherry tomatoes\":2,\n  \"parsley\":8,\n  \"molasses\":3,\n  \"chili powder\":5,\n  \"raisins\":17,\n  \"red wine vinegar\":4,\n  \"cornstarch\":22,\n  \"orange\":6,\n  \"catsup\":2,\n  \"ground beef\":2,\n  \"sweet red pepper\":2,\n  \"eggs\":27,\n  \"almonds\":3,\n  \"margarine\":21,\n  \"garlic powder\":3,\n  \"dried thyme\":4,\n  \"cream of mushroom soup\":2,\n  \"vanilla\":8,\n  \"whole wheat flour\":3,\n  \"salt\":132,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":5,\n  \"allspice\":8,\n  \"dijon mustard\":4,\n  \"soy sauce\":6,\n  \"broccoli\":3,\n  \"apples\":14,\n  \"chicken legs\":2,\n  \"maple syrup\":8,\n  \"egg whites\":6,\n  \"worcestershire sauce\":4,\n  \"brown sugar\":47,\n  \"lime juice\":2,\n  \"dry mustard\":3,\n  \"mayonnaise\":2,\n  \"vegetable oil\":17,\n  \"olive oil\":27,\n  \"fresh thyme\":3,\n  \"curry powder\":14,\n  \"flour\":26,\n  \"vinegar\":6,\n  \"sherry\":4,\n  \"whipping cream\":4,\n  \"sugar\":56},\n \"yam\":\n {\"cornstarch\":2,\n  \"dried basil\":2,\n  \"balsamic vinegar\":2,\n  \"ground beef\":2,\n  \"cherry tomatoes\":3,\n  \"green onions\":2,\n  \"cloves\":2,\n  \"vegetable oil\":2,\n  \"olive oil\":2,\n  \"flour\":2,\n  \"margarine\":2,\n  \"canola oil\":2,\n  \"dijon mustard\":2,\n  \"fresh ginger\":2,\n  \"butter\":2,\n  \"sherry\":2,\n  \"carrots\":2,\n  \"nutmeg\":2,\n  \"salt\":2,\n  \"mushrooms\":2,\n  \"black pepper\":3},\n \"fresh basil\":\n {\"red wine\":10,\n  \"elbow macaroni\":2,\n  \"canned tomato sauce\":2,\n  \"bacon\":4,\n  \"cayenne pepper\":8,\n  \"lemon juice\":31,\n  \"honey\":6,\n  \"paprika\":3,\n  \"yeast\":2,\n  \"egg yolks\":4,\n  \"green pepper\":8,\n  \"baking powder\":5,\n  \"saffron\":2,\n  \"sesame oil\":4,\n  \"pine nuts\":15,\n  \"cinnamon\":2,\n  \"tomatoes\":52,\n  \"white pepper\":10,\n  \"black beans\":2,\n  \"fresh garlic\":3,\n  \"cauliflower\":2,\n  \"butter\":45,\n  \"frozen peas\":2,\n  \"orange juice\":2,\n  \"black pepper\":28,\n  \"lemon\":11,\n  \"black olives\":13,\n  \"buttermilk\":4,\n  \"hoisin sauce\":2,\n  \"eggplant\":20,\n  \"dried oregano\":19,\n  \"capers\":10,\n  \"cornmeal\":3,\n  \"cloves\":2,\n  \"oil\":13,\n  \"chicken\":6,\n  \"blue cheese\":2,\n  \"cucumber\":2,\n  \"flour tortilla\":2,\n  \"celery\":10,\n  \"walnuts\":2,\n  \"nutmeg\":6,\n  \"fresh cilantro\":4,\n  \"baking soda\":3,\n  \"bread crumbs\":4,\n  \"tabasco sauce\":2,\n  \"balsamic vinegar\":20,\n  \"fresh rosemary\":11,\n  \"frozen corn\":2,\n  \"cumin\":3,\n  \"green onions\":14,\n  \"skim milk\":2,\n  \"parmesan cheese\":53,\n  \"carrots\":17,\n  \"mushrooms\":20,\n  \"sour cream\":10,\n  \"tofu\":2,\n  \"cream cheese\":7,\n  \"cherry tomatoes\":10,\n  \"parsley\":16,\n  \"rice vinegar\":3,\n  \"beer\":2,\n  \"chili powder\":2,\n  \"spaghetti\":8,\n  \"raisins\":2,\n  \"red wine vinegar\":24,\n  \"chives\":5,\n  \"cornstarch\":6,\n  \"dried basil\":28,\n  \"ground beef\":4,\n  \"mozzarella cheese\":18,\n  \"sweet red pepper\":9,\n  \"red pepper flakes\":9,\n  \"eggs\":17,\n  \"margarine\":7,\n  \"fresh ginger\":3,\n  \"garlic powder\":8,\n  \"dried thyme\":9,\n  \"whole wheat flour\":4,\n  \"salt\":141,\n  \"bean sprouts\":2,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":5,\n  \"frozen spinach\":2,\n  \"allspice\":2,\n  \"celery seeds\":3,\n  \"dijon mustard\":17,\n  \"soy sauce\":9,\n  \"broccoli\":4,\n  \"chicken legs\":2,\n  \"maple syrup\":2,\n  \"white rice\":2,\n  \"egg whites\":4,\n  \"worcestershire sauce\":7,\n  \"brown sugar\":6,\n  \"lime juice\":4,\n  \"dry mustard\":4,\n  \"mayonnaise\":7,\n  \"olive oil\":199,\n  \"vegetable oil\":23,\n  \"fresh thyme\":20,\n  \"flour\":10,\n  \"curry powder\":2,\n  \"vinegar\":2,\n  \"white wine\":14,\n  \"whipping cream\":11,\n  \"chicken breast\":4,\n  \"sugar\":31},\n \"cashew\":{\"lemon\":2, \"salt\":2},\n \"banana\":\n {\"sour cream\":17,\n  \"yogurt\":7,\n  \"cream cheese\":3,\n  \"parsley\":2,\n  \"cayenne pepper\":4,\n  \"honey\":31,\n  \"lemon juice\":22,\n  \"molasses\":2,\n  \"paprika\":3,\n  \"chili powder\":2,\n  \"bread\":2,\n  \"ground cinnamon\":10,\n  \"raisins\":9,\n  \"red wine vinegar\":2,\n  \"yeast\":14,\n  \"baking powder\":37,\n  \"cornstarch\":5,\n  \"orange\":12,\n  \"pine nuts\":2,\n  \"cream of tartar\":5,\n  \"cinnamon\":29,\n  \"tomatoes\":4,\n  \"cumin seeds\":2,\n  \"white pepper\":2,\n  \"almonds\":6,\n  \"eggs\":18,\n  \"pinto beans\":2,\n  \"margarine\":7,\n  \"fresh ginger\":3,\n  \"butter\":30,\n  \"vanilla\":33,\n  \"orange juice\":35,\n  \"whole wheat flour\":18,\n  \"black pepper\":4,\n  \"lemon\":3,\n  \"salt\":72,\n  \"potatoes\":2,\n  \"buttermilk\":7,\n  \"powdered sugar\":7,\n  \"almond extract\":4,\n  \"cornmeal\":2,\n  \"cloves\":3,\n  \"oil\":11,\n  \"blue cheese\":2,\n  \"cucumber\":2,\n  \"allspice\":2,\n  \"canola oil\":4,\n  \"soy sauce\":3,\n  \"strawberries\":24,\n  \"broccoli\":2,\n  \"apples\":6,\n  \"maple syrup\":3,\n  \"walnuts\":15,\n  \"nutmeg\":10,\n  \"egg whites\":7,\n  \"baking soda\":36,\n  \"corn syrup\":2,\n  \"brown sugar\":17,\n  \"balsamic vinegar\":3,\n  \"ginger\":4,\n  \"lime juice\":5,\n  \"lime\":2,\n  \"pecans\":9,\n  \"blueberries\":13,\n  \"peanut butter\":9,\n  \"mayonnaise\":2,\n  \"cumin\":2,\n  \"green onions\":2,\n  \"vegetable oil\":17,\n  \"olive oil\":3,\n  \"skim milk\":11,\n  \"flour\":33,\n  \"curry powder\":12,\n  \"vinegar\":2,\n  \"shortening\":4,\n  \"white wine\":2,\n  \"parmesan cheese\":2,\n  \"applesauce\":5,\n  \"carrots\":4,\n  \"oats\":3,\n  \"whipping cream\":7,\n  \"mushrooms\":2,\n  \"sugar\":72},\n \"canned chicken broth\":\n {\"sour cream\":4,\n  \"cayenne pepper\":2,\n  \"lemon juice\":6,\n  \"honey\":3,\n  \"swiss cheese\":3,\n  \"paprika\":3,\n  \"chili powder\":2,\n  \"cornstarch\":10,\n  \"dried basil\":5,\n  \"saffron\":2,\n  \"catsup\":2,\n  \"pine nuts\":2,\n  \"sweet red pepper\":3,\n  \"tomatoes\":2,\n  \"white pepper\":3,\n  \"brown rice\":2,\n  \"red pepper flakes\":2,\n  \"eggs\":2,\n  \"fresh ginger\":2,\n  \"frozen peas\":3,\n  \"butter\":6,\n  \"garlic powder\":2,\n  \"dried thyme\":4,\n  \"salt\":19,\n  \"black pepper\":2,\n  \"lemon\":3,\n  \"potatoes\":2,\n  \"almond extract\":2,\n  \"dried oregano\":6,\n  \"oil\":3,\n  \"soy sauce\":3,\n  \"celery\":2,\n  \"chicken legs\":2,\n  \"sweet potatoes\":2,\n  \"egg whites\":2,\n  \"baking soda\":2,\n  \"lime juice\":2,\n  \"dry mustard\":2,\n  \"green onions\":5,\n  \"olive oil\":4,\n  \"vegetable oil\":8,\n  \"curry powder\":2,\n  \"flour\":3,\n  \"vinegar\":2,\n  \"white wine\":2,\n  \"carrots\":2,\n  \"chicken breast\":3,\n  \"sugar\":5,\n  \"mushrooms\":2},\n \"canola oil\":\n {\"bacon\":4,\n  \"cayenne pepper\":17,\n  \"lemon juice\":31,\n  \"honey\":58,\n  \"paprika\":18,\n  \"bread\":3,\n  \"ground cinnamon\":17,\n  \"yeast\":25,\n  \"egg yolks\":6,\n  \"green pepper\":5,\n  \"baking powder\":79,\n  \"saffron\":2,\n  \"sesame oil\":10,\n  \"pine nuts\":4,\n  \"cinnamon\":29,\n  \"cream of tartar\":6,\n  \"avocado\":4,\n  \"cheddar cheese\":4,\n  \"tomatoes\":23,\n  \"brown rice\":2,\n  \"black beans\":4,\n  \"fresh garlic\":3,\n  \"pinto beans\":2,\n  \"cauliflower\":3,\n  \"frozen peas\":7,\n  \"butter\":36,\n  \"orange juice\":20,\n  \"lemon\":2,\n  \"black pepper\":24,\n  \"tumeric\":3,\n  \"black olives\":2,\n  \"buttermilk\":18,\n  \"powdered sugar\":3,\n  \"dried oregano\":16,\n  \"hoisin sauce\":3,\n  \"cornmeal\":6,\n  \"cloves\":5,\n  \"oil\":3,\n  \"chicken\":2,\n  \"cucumber\":4,\n  \"dried tarragon\":2,\n  \"strawberries\":2,\n  \"celery\":7,\n  \"alfalfa sprouts\":2,\n  \"walnuts\":11,\n  \"nutmeg\":17,\n  \"fresh cilantro\":8,\n  \"baking soda\":56,\n  \"bread crumbs\":8,\n  \"tabasco sauce\":4,\n  \"balsamic vinegar\":8,\n  \"ginger\":8,\n  \"fresh rosemary\":8,\n  \"lime\":2,\n  \"pecans\":3,\n  \"peanut butter\":2,\n  \"frozen corn\":3,\n  \"green onions\":13,\n  \"cumin\":13,\n  \"beets\":2,\n  \"skim milk\":26,\n  \"banana\":4,\n  \"barley\":3,\n  \"kidney beans\":2,\n  \"applesauce\":6,\n  \"parmesan cheese\":5,\n  \"carrots\":25,\n  \"mushrooms\":17,\n  \"sour cream\":10,\n  \"yogurt\":2,\n  \"brussels sprouts\":3,\n  \"tofu\":7,\n  \"cherry tomatoes\":3,\n  \"salsa\":7,\n  \"parsley\":9,\n  \"rice vinegar\":6,\n  \"molasses\":13,\n  \"chili powder\":27,\n  \"beer\":3,\n  \"raisins\":16,\n  \"red wine vinegar\":9,\n  \"garam masala\":5,\n  \"chives\":4,\n  \"cornstarch\":12,\n  \"dried basil\":4,\n  \"orange\":4,\n  \"catsup\":2,\n  \"ground beef\":4,\n  \"mozzarella cheese\":2,\n  \"romaine lettuce\":2,\n  \"cumin seeds\":6,\n  \"red pepper flakes\":2,\n  \"almonds\":4,\n  \"eggs\":50,\n  \"margarine\":5,\n  \"fresh ginger\":8,\n  \"garlic powder\":6,\n  \"dried thyme\":13,\n  \"vanilla\":21,\n  \"whole wheat flour\":37,\n  \"salt\":263,\n  \"shiitake mushrooms\":4,\n  \"potatoes\":11,\n  \"almond extract\":5,\n  \"allspice\":4,\n  \"dijon mustard\":11,\n  \"soy sauce\":31,\n  \"broccoli\":3,\n  \"apples\":10,\n  \"sweet potatoes\":3,\n  \"maple syrup\":11,\n  \"egg whites\":31,\n  \"worcestershire sauce\":14,\n  \"dried cilantro\":2,\n  \"brown sugar\":17,\n  \"lime juice\":13,\n  \"dry mustard\":8,\n  \"white flour\":3,\n  \"blueberries\":7,\n  \"chicken wings\":2,\n  \"fresh thyme\":5,\n  \"vegetable oil\":2,\n  \"olive oil\":13,\n  \"curry powder\":17,\n  \"flour\":55,\n  \"vinegar\":11,\n  \"white wine\":3,\n  \"sherry\":4,\n  \"oats\":4,\n  \"yam\":2,\n  \"chicken breast\":6,\n  \"sugar\":100},\n \"flour\":\n {\"canned beef broth\":2,\n  \"elbow macaroni\":19,\n  \"red wine\":68,\n  \"bacon\":143,\n  \"brown onions\":2,\n  \"cayenne pepper\":165,\n  \"honey\":315,\n  \"lemon juice\":619,\n  \"swiss cheese\":61,\n  \"paprika\":428,\n  \"bread\":32,\n  \"ground cinnamon\":200,\n  \"yeast\":410,\n  \"egg yolks\":388,\n  \"green pepper\":140,\n  \"baking powder\":3076,\n  \"saffron\":10,\n  \"sesame oil\":35,\n  \"pine nuts\":23,\n  \"cream of tartar\":154,\n  \"cinnamon\":1504,\n  \"avocado\":7,\n  \"cheddar cheese\":150,\n  \"corn tortilla\":2,\n  \"tomatoes\":173,\n  \"white pepper\":140,\n  \"brown rice\":4,\n  \"black beans\":5,\n  \"pinto beans\":4,\n  \"fresh garlic\":4,\n  \"cauliflower\":14,\n  \"frozen peas\":22,\n  \"butter\":4221,\n  \"salmon\":16,\n  \"orange juice\":199,\n  \"lemon\":191,\n  \"black pepper\":266,\n  \"tumeric\":7,\n  \"black olives\":9,\n  \"buttermilk\":391,\n  \"powdered sugar\":341,\n  \"hoisin sauce\":3,\n  \"eggplant\":35,\n  \"dried oregano\":40,\n  \"capers\":30,\n  \"cornmeal\":160,\n  \"cloves\":277,\n  \"oil\":957,\n  \"chicken\":131,\n  \"blue cheese\":6,\n  \"cucumber\":4,\n  \"flour tortilla\":2,\n  \"canola oil\":55,\n  \"dried tarragon\":11,\n  \"strawberries\":44,\n  \"celery\":185,\n  \"fresh peaches\":12,\n  \"fresh cilantro\":5,\n  \"nutmeg\":785,\n  \"walnuts\":327,\n  \"baking soda\":1969,\n  \"bread crumbs\":185,\n  \"corn syrup\":40,\n  \"tabasco sauce\":61,\n  \"balsamic vinegar\":20,\n  \"ginger\":217,\n  \"fresh rosemary\":4,\n  \"lime\":6,\n  \"pecans\":257,\n  \"peanut butter\":123,\n  \"frozen corn\":7,\n  \"green onions\":99,\n  \"cumin\":71,\n  \"canned chicken broth\":3,\n  \"beets\":8,\n  \"skim milk\":148,\n  \"banana\":33,\n  \"barley\":6,\n  \"kidney beans\":7,\n  \"shortening\":867,\n  \"parmesan cheese\":204,\n  \"applesauce\":124,\n  \"carrots\":282,\n  \"mushrooms\":300,\n  \"sour cream\":522,\n  \"yogurt\":44,\n  \"brussels sprouts\":2,\n  \"cream cheese\":282,\n  \"tofu\":15,\n  \"cherry tomatoes\":4,\n  \"salsa\":5,\n  \"parsley\":362,\n  \"green olives\":7,\n  \"rice vinegar\":3,\n  \"molasses\":214,\n  \"chili powder\":126,\n  \"beer\":88,\n  \"spaghetti\":16,\n  \"raisins\":590,\n  \"red wine vinegar\":39,\n  \"garam masala\":3,\n  \"chives\":37,\n  \"cornstarch\":243,\n  \"orange\":38,\n  \"dried basil\":35,\n  \"catsup\":43,\n  \"radishes\":2,\n  \"ground beef\":92,\n  \"mozzarella cheese\":45,\n  \"sweet red pepper\":8,\n  \"cumin seeds\":8,\n  \"canned peaches\":2,\n  \"barbecue sauce\":8,\n  \"raw spinach\":3,\n  \"red pepper flakes\":11,\n  \"eggs\":3597,\n  \"almonds\":162,\n  \"margarine\":662,\n  \"fresh ginger\":8,\n  \"garlic powder\":164,\n  \"dried thyme\":72,\n  \"cream of mushroom soup\":31,\n  \"low fat milk\":7,\n  \"vanilla\":2196,\n  \"whole wheat flour\":76,\n  \"salt\":7567,\n  \"fresh basil\":10,\n  \"bean sprouts\":5,\n  \"shiitake mushrooms\":3,\n  \"potatoes\":263,\n  \"almond extract\":227,\n  \"frozen spinach\":18,\n  \"canned apricots\":2,\n  \"celery seeds\":3,\n  \"allspice\":165,\n  \"dijon mustard\":61,\n  \"soy sauce\":127,\n  \"broccoli\":35,\n  \"chicken legs\":3,\n  \"apples\":225,\n  \"white rice\":3,\n  \"yellow mustard\":2,\n  \"shell macaroni\":2,\n  \"sweet potatoes\":26,\n  \"maple syrup\":60,\n  \"canned tomato paste\":3,\n  \"red leaf lettuce\":2,\n  \"egg whites\":323,\n  \"worcestershire sauce\":237,\n  \"dried cilantro\":2,\n  \"brown sugar\":1252,\n  \"lime juice\":25,\n  \"dry mustard\":169,\n  \"raw shrimp\":4,\n  \"white flour\":4,\n  \"blueberries\":86,\n  \"mayonnaise\":61,\n  \"chicken wings\":15,\n  \"vegetable oil\":537,\n  \"olive oil\":455,\n  \"fresh thyme\":6,\n  \"curry powder\":116,\n  \"vinegar\":208,\n  \"white wine\":128,\n  \"sherry\":137,\n  \"yam\":2,\n  \"oats\":105,\n  \"whipping cream\":145,\n  \"chicken breast\":24,\n  \"sugar\":5965},\n \"margarine\":\n {\"red wine\":5,\n  \"elbow macaroni\":5,\n  \"canned tomato sauce\":2,\n  \"bacon\":13,\n  \"cayenne pepper\":37,\n  \"lemon juice\":184,\n  \"honey\":95,\n  \"swiss cheese\":22,\n  \"paprika\":83,\n  \"ground cinnamon\":133,\n  \"bread\":11,\n  \"yeast\":77,\n  \"egg yolks\":44,\n  \"green pepper\":40,\n  \"baking powder\":372,\n  \"saffron\":5,\n  \"pine nuts\":4,\n  \"cream of tartar\":26,\n  \"cinnamon\":263,\n  \"cheddar cheese\":57,\n  \"tomatoes\":37,\n  \"white pepper\":40,\n  \"brown rice\":5,\n  \"black beans\":2,\n  \"fresh garlic\":2,\n  \"cauliflower\":2,\n  \"frozen peas\":4,\n  \"butter\":39,\n  \"salmon\":3,\n  \"orange juice\":64,\n  \"lemon\":24,\n  \"black pepper\":91,\n  \"tumeric\":4,\n  \"black olives\":7,\n  \"buttermilk\":55,\n  \"powdered sugar\":138,\n  \"eggplant\":12,\n  \"dried oregano\":11,\n  \"capers\":5,\n  \"cornmeal\":21,\n  \"cloves\":37,\n  \"oil\":39,\n  \"chicken\":27,\n  \"cucumber\":2,\n  \"canola oil\":5,\n  \"flour tortilla\":2,\n  \"dried tarragon\":4,\n  \"strawberries\":13,\n  \"celery\":68,\n  \"cornish game hens\":3,\n  \"fresh peaches\":2,\n  \"fresh cilantro\":5,\n  \"walnuts\":62,\n  \"nutmeg\":121,\n  \"baking soda\":349,\n  \"bread crumbs\":55,\n  \"corn syrup\":27,\n  \"tabasco sauce\":19,\n  \"balsamic vinegar\":6,\n  \"ginger\":37,\n  \"fresh rosemary\":3,\n  \"pecans\":66,\n  \"peanut butter\":48,\n  \"green onions\":32,\n  \"cumin\":15,\n  \"frozen corn\":6,\n  \"skim milk\":139,\n  \"banana\":7,\n  \"beets\":3,\n  \"kidney beans\":3,\n  \"shortening\":36,\n  \"parmesan cheese\":64,\n  \"applesauce\":22,\n  \"carrots\":71,\n  \"mushrooms\":61,\n  \"sour cream\":106,\n  \"yogurt\":11,\n  \"brussels sprouts\":2,\n  \"tofu\":4,\n  \"cream cheese\":125,\n  \"cherry tomatoes\":2,\n  \"salsa\":3,\n  \"parsley\":84,\n  \"green olives\":2,\n  \"molasses\":38,\n  \"chili powder\":37,\n  \"beer\":6,\n  \"spaghetti\":3,\n  \"raisins\":125,\n  \"red wine vinegar\":5,\n  \"garam masala\":3,\n  \"chives\":14,\n  \"cornstarch\":97,\n  \"orange\":15,\n  \"dried basil\":23,\n  \"radishes\":3,\n  \"catsup\":9,\n  \"ground beef\":13,\n  \"mozzarella cheese\":18,\n  \"sweet red pepper\":4,\n  \"red pepper flakes\":3,\n  \"almonds\":32,\n  \"eggs\":474,\n  \"fresh ginger\":2,\n  \"garlic powder\":74,\n  \"dried thyme\":21,\n  \"cream of mushroom soup\":19,\n  \"low fat milk\":7,\n  \"vanilla\":426,\n  \"whole wheat flour\":63,\n  \"salt\":1259,\n  \"fresh basil\":7,\n  \"bean sprouts\":2,\n  \"potatoes\":66,\n  \"almond extract\":55,\n  \"frozen spinach\":6,\n  \"celery seeds\":2,\n  \"allspice\":25,\n  \"dijon mustard\":16,\n  \"soy sauce\":26,\n  \"broccoli\":10,\n  \"chicken legs\":2,\n  \"apples\":57,\n  \"yellow mustard\":2,\n  \"white rice\":2,\n  \"maple syrup\":22,\n  \"sweet potatoes\":21,\n  \"egg whites\":107,\n  \"worcestershire sauce\":59,\n  \"brown sugar\":309,\n  \"lime juice\":12,\n  \"dry mustard\":31,\n  \"white flour\":15,\n  \"blueberries\":18,\n  \"mayonnaise\":13,\n  \"chicken wings\":3,\n  \"fresh thyme\":3,\n  \"olive oil\":52,\n  \"vegetable oil\":46,\n  \"flour\":662,\n  \"curry powder\":29,\n  \"vinegar\":27,\n  \"white wine\":20,\n  \"sherry\":14,\n  \"yam\":2,\n  \"oats\":30,\n  \"whipping cream\":30,\n  \"chicken breast\":11,\n  \"sugar\":1005},\n \"red wine vinegar\":\n {\"elbow macaroni\":3,\n  \"red wine\":19,\n  \"bacon\":18,\n  \"cayenne pepper\":34,\n  \"lemon juice\":70,\n  \"honey\":49,\n  \"swiss cheese\":2,\n  \"paprika\":49,\n  \"bread\":3,\n  \"ground cinnamon\":14,\n  \"egg yolks\":9,\n  \"green pepper\":34,\n  \"saffron\":2,\n  \"sesame oil\":17,\n  \"pine nuts\":14,\n  \"cinnamon\":17,\n  \"avocado\":8,\n  \"cheddar cheese\":2,\n  \"tomatoes\":83,\n  \"white pepper\":19,\n  \"brown rice\":4,\n  \"black beans\":11,\n  \"pinto beans\":3,\n  \"frozen peas\":6,\n  \"butter\":52,\n  \"salmon\":2,\n  \"orange juice\":18,\n  \"black pepper\":79,\n  \"lemon\":19,\n  \"tumeric\":2,\n  \"black olives\":13,\n  \"buttermilk\":5,\n  \"dried oregano\":48,\n  \"eggplant\":15,\n  \"hoisin sauce\":2,\n  \"capers\":41,\n  \"cornmeal\":2,\n  \"cloves\":18,\n  \"oil\":41,\n  \"chicken\":13,\n  \"blue cheese\":5,\n  \"cucumber\":26,\n  \"canola oil\":9,\n  \"flour tortilla\":2,\n  \"dried tarragon\":10,\n  \"celery\":21,\n  \"alfalfa sprouts\":5,\n  \"walnuts\":4,\n  \"fresh cilantro\":11,\n  \"nutmeg\":9,\n  \"bread crumbs\":3,\n  \"corn syrup\":2,\n  \"tabasco sauce\":21,\n  \"balsamic vinegar\":13,\n  \"ginger\":21,\n  \"fresh rosemary\":6,\n  \"lime\":5,\n  \"pecans\":3,\n  \"peanut butter\":2,\n  \"cumin\":15,\n  \"green onions\":36,\n  \"banana\":2,\n  \"skim milk\":3,\n  \"beets\":12,\n  \"kidney beans\":4,\n  \"shortening\":3,\n  \"parmesan cheese\":17,\n  \"carrots\":32,\n  \"mushrooms\":15,\n  \"sour cream\":19,\n  \"brussels sprouts\":3,\n  \"tofu\":2,\n  \"cream cheese\":4,\n  \"cherry tomatoes\":17,\n  \"salsa\":3,\n  \"parsley\":48,\n  \"green olives\":5,\n  \"rice vinegar\":4,\n  \"molasses\":20,\n  \"beer\":13,\n  \"chili powder\":44,\n  \"spaghetti\":5,\n  \"raisins\":18,\n  \"chives\":5,\n  \"cornstarch\":37,\n  \"dried basil\":32,\n  \"orange\":5,\n  \"radishes\":4,\n  \"catsup\":17,\n  \"ground beef\":5,\n  \"mozzarella cheese\":7,\n  \"romaine lettuce\":5,\n  \"sweet red pepper\":5,\n  \"cumin seeds\":9,\n  \"red pepper flakes\":14,\n  \"almonds\":2,\n  \"eggs\":14,\n  \"margarine\":5,\n  \"fresh ginger\":8,\n  \"garlic powder\":43,\n  \"dried thyme\":24,\n  \"cream of mushroom soup\":2,\n  \"salt\":432,\n  \"fresh basil\":24,\n  \"bean sprouts\":2,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":6,\n  \"allspice\":8,\n  \"celery seeds\":6,\n  \"dijon mustard\":100,\n  \"soy sauce\":87,\n  \"broccoli\":8,\n  \"apples\":5,\n  \"maple syrup\":3,\n  \"yellow mustard\":8,\n  \"sweet potatoes\":4,\n  \"egg whites\":3,\n  \"worcestershire sauce\":79,\n  \"brown sugar\":76,\n  \"lime juice\":22,\n  \"dry mustard\":48,\n  \"raw shrimp\":2,\n  \"mayonnaise\":21,\n  \"chicken wings\":3,\n  \"vegetable oil\":101,\n  \"olive oil\":390,\n  \"fresh thyme\":10,\n  \"curry powder\":6,\n  \"flour\":39,\n  \"vinegar\":2,\n  \"white wine\":14,\n  \"sherry\":9,\n  \"whipping cream\":2,\n  \"chicken breast\":5,\n  \"sugar\":165},\n \"eggplant\":\n {\"elbow macaroni\":2,\n  \"red wine\":6,\n  \"bacon\":4,\n  \"cayenne pepper\":15,\n  \"lemon juice\":42,\n  \"honey\":7,\n  \"zucchini squash\":2,\n  \"swiss cheese\":5,\n  \"paprika\":17,\n  \"bread\":2,\n  \"ground cinnamon\":4,\n  \"dried bay leaf\":2,\n  \"yeast\":2,\n  \"egg yolks\":4,\n  \"green pepper\":51,\n  \"baking powder\":6,\n  \"sesame oil\":5,\n  \"pine nuts\":10,\n  \"cinnamon\":12,\n  \"avocado\":2,\n  \"cheddar cheese\":6,\n  \"tomatoes\":94,\n  \"white pepper\":7,\n  \"brown rice\":4,\n  \"cauliflower\":5,\n  \"frozen peas\":2,\n  \"butter\":35,\n  \"lemon\":13,\n  \"black pepper\":41,\n  \"tumeric\":4,\n  \"black olives\":8,\n  \"dried oregano\":24,\n  \"hoisin sauce\":2,\n  \"capers\":11,\n  \"cornmeal\":3,\n  \"cloves\":8,\n  \"oil\":48,\n  \"chicken\":7,\n  \"flour tortilla\":2,\n  \"celery\":9,\n  \"walnuts\":2,\n  \"fresh cilantro\":5,\n  \"nutmeg\":10,\n  \"bread crumbs\":23,\n  \"baking soda\":3,\n  \"tabasco sauce\":3,\n  \"balsamic vinegar\":22,\n  \"ginger\":12,\n  \"lime\":2,\n  \"peanut butter\":4,\n  \"cumin\":19,\n  \"frozen corn\":2,\n  \"green onions\":6,\n  \"skim milk\":5,\n  \"parmesan cheese\":37,\n  \"applesauce\":2,\n  \"carrots\":18,\n  \"mushrooms\":25,\n  \"sour cream\":4,\n  \"yogurt\":5,\n  \"tofu\":3,\n  \"cherry tomatoes\":5,\n  \"salsa\":4,\n  \"parsley\":34,\n  \"green olives\":5,\n  \"molasses\":2,\n  \"chili powder\":12,\n  \"beer\":2,\n  \"spaghetti\":3,\n  \"raisins\":5,\n  \"red wine vinegar\":15,\n  \"garam masala\":3,\n  \"cornstarch\":8,\n  \"dried basil\":16,\n  \"ground beef\":14,\n  \"romaine lettuce\":2,\n  \"mozzarella cheese\":34,\n  \"sweet red pepper\":5,\n  \"cumin seeds\":5,\n  \"red pepper flakes\":6,\n  \"almonds\":2,\n  \"eggs\":38,\n  \"margarine\":12,\n  \"fresh ginger\":3,\n  \"garlic powder\":14,\n  \"dried thyme\":7,\n  \"whole wheat flour\":2,\n  \"salt\":277,\n  \"fresh basil\":20,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":18,\n  \"frozen spinach\":2,\n  \"allspice\":4,\n  \"dijon mustard\":2,\n  \"soy sauce\":16,\n  \"broccoli\":2,\n  \"sweet potatoes\":2,\n  \"egg whites\":5,\n  \"worcestershire sauce\":4,\n  \"brown sugar\":3,\n  \"lime juice\":2,\n  \"raw shrimp\":2,\n  \"mayonnaise\":4,\n  \"vegetable oil\":26,\n  \"olive oil\":191,\n  \"flour\":35,\n  \"curry powder\":8,\n  \"vinegar\":19,\n  \"white wine\":2,\n  \"sherry\":4,\n  \"oats\":3,\n  \"whipping cream\":2,\n  \"chicken breast\":2,\n  \"sugar\":56},\n \"worcestershire sauce\":\n {\"red wine\":21,\n  \"elbow macaroni\":6,\n  \"canned tomato sauce\":2,\n  \"bacon\":65,\n  \"cayenne pepper\":124,\n  \"honey\":65,\n  \"lemon juice\":322,\n  \"swiss cheese\":12,\n  \"paprika\":248,\n  \"camembert cheese\":2,\n  \"ground cinnamon\":8,\n  \"bread\":11,\n  \"yeast\":4,\n  \"egg yolks\":15,\n  \"green pepper\":103,\n  \"baking powder\":8,\n  \"sesame oil\":9,\n  \"cinnamon\":19,\n  \"pine nuts\":4,\n  \"avocado\":8,\n  \"cheddar cheese\":56,\n  \"tomatoes\":70,\n  \"white pepper\":46,\n  \"brown rice\":2,\n  \"pinto beans\":4,\n  \"fresh garlic\":4,\n  \"cauliflower\":4,\n  \"frozen peas\":5,\n  \"butter\":324,\n  \"salmon\":6,\n  \"orange juice\":9,\n  \"black pepper\":183,\n  \"lemon\":75,\n  \"black olives\":5,\n  \"buttermilk\":10,\n  \"powdered sugar\":3,\n  \"hoisin sauce\":4,\n  \"dried oregano\":36,\n  \"eggplant\":4,\n  \"capers\":17,\n  \"cornmeal\":6,\n  \"cloves\":17,\n  \"oil\":119,\n  \"chicken\":22,\n  \"blue cheese\":12,\n  \"cucumber\":11,\n  \"canola oil\":14,\n  \"dried tarragon\":6,\n  \"strawberries\":2,\n  \"celery\":93,\n  \"fresh cilantro\":3,\n  \"nutmeg\":11,\n  \"walnuts\":7,\n  \"baking soda\":3,\n  \"bread crumbs\":63,\n  \"tabasco sauce\":181,\n  \"corn syrup\":4,\n  \"balsamic vinegar\":11,\n  \"ginger\":19,\n  \"fresh rosemary\":2,\n  \"lime\":3,\n  \"pecans\":9,\n  \"peanut butter\":7,\n  \"cumin\":35,\n  \"frozen corn\":8,\n  \"green onions\":75,\n  \"skim milk\":21,\n  \"barley\":5,\n  \"kidney beans\":9,\n  \"shortening\":18,\n  \"parmesan cheese\":56,\n  \"applesauce\":2,\n  \"carrots\":85,\n  \"mushrooms\":82,\n  \"sour cream\":99,\n  \"yogurt\":4,\n  \"tofu\":6,\n  \"cream cheese\":82,\n  \"frozen french fries\":2,\n  \"cherry tomatoes\":6,\n  \"salsa\":8,\n  \"parsley\":118,\n  \"green olives\":6,\n  \"rice vinegar\":3,\n  \"molasses\":48,\n  \"chili powder\":190,\n  \"beer\":38,\n  \"spaghetti\":8,\n  \"raisins\":10,\n  \"red wine vinegar\":79,\n  \"garam masala\":2,\n  \"chives\":9,\n  \"cornstarch\":50,\n  \"orange\":4,\n  \"dried basil\":28,\n  \"catsup\":199,\n  \"ground beef\":113,\n  \"romaine lettuce\":3,\n  \"mozzarella cheese\":8,\n  \"sweet red pepper\":3,\n  \"canned tuna\":2,\n  \"cumin seeds\":3,\n  \"barbecue sauce\":12,\n  \"red pepper flakes\":8,\n  \"eggs\":121,\n  \"almonds\":4,\n  \"margarine\":59,\n  \"fresh ginger\":2,\n  \"garlic powder\":192,\n  \"dried thyme\":39,\n  \"cream of mushroom soup\":23,\n  \"low fat milk\":2,\n  \"whole wheat flour\":2,\n  \"salt\":1000,\n  \"fresh basil\":7,\n  \"bean sprouts\":2,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":66,\n  \"frozen spinach\":2,\n  \"celery seeds\":5,\n  \"allspice\":16,\n  \"dijon mustard\":96,\n  \"soy sauce\":133,\n  \"broccoli\":2,\n  \"chicken legs\":2,\n  \"apples\":6,\n  \"sweet potatoes\":4,\n  \"yellow mustard\":9,\n  \"maple syrup\":12,\n  \"white rice\":3,\n  \"canned tomato paste\":2,\n  \"egg whites\":7,\n  \"brown sugar\":272,\n  \"lime juice\":21,\n  \"dry mustard\":292,\n  \"raw shrimp\":2,\n  \"white flour\":2,\n  \"mayonnaise\":164,\n  \"chicken wings\":16,\n  \"fresh thyme\":4,\n  \"olive oil\":160,\n  \"vegetable oil\":130,\n  \"flour\":237,\n  \"curry powder\":32,\n  \"vinegar\":228,\n  \"white wine\":34,\n  \"sherry\":26,\n  \"whipping cream\":26,\n  \"chicken breast\":4,\n  \"sugar\":197},\n \"soy sauce\":\n {\"canned beef broth\":5,\n  \"red wine\":18,\n  \"bacon\":36,\n  \"cayenne pepper\":65,\n  \"honey\":315,\n  \"lemon juice\":268,\n  \"swiss cheese\":3,\n  \"paprika\":62,\n  \"bread\":5,\n  \"ground cinnamon\":22,\n  \"yeast\":4,\n  \"egg yolks\":4,\n  \"green pepper\":132,\n  \"baking powder\":13,\n  \"saffron\":2,\n  \"sesame oil\":554,\n  \"pine nuts\":8,\n  \"cinnamon\":27,\n  \"avocado\":3,\n  \"cheddar cheese\":6,\n  \"tomatoes\":61,\n  \"white pepper\":70,\n  \"brown rice\":24,\n  \"black beans\":9,\n  \"fresh garlic\":7,\n  \"pinto beans\":4,\n  \"cauliflower\":12,\n  \"frozen peas\":15,\n  \"butter\":108,\n  \"salmon\":2,\n  \"orange juice\":77,\n  \"black pepper\":173,\n  \"lemon\":37,\n  \"tumeric\":6,\n  \"buttermilk\":2,\n  \"powdered sugar\":3,\n  \"eggplant\":16,\n  \"hoisin sauce\":100,\n  \"dried oregano\":14,\n  \"capers\":3,\n  \"cornmeal\":4,\n  \"cloves\":16,\n  \"oil\":896,\n  \"chicken\":73,\n  \"blue cheese\":4,\n  \"cucumber\":33,\n  \"canola oil\":31,\n  \"dried tarragon\":7,\n  \"celery\":144,\n  \"alfalfa sprouts\":4,\n  \"cornish game hens\":7,\n  \"walnuts\":11,\n  \"nutmeg\":24,\n  \"fresh cilantro\":29,\n  \"baking soda\":10,\n  \"bread crumbs\":24,\n  \"tabasco sauce\":56,\n  \"corn syrup\":5,\n  \"ginger\":308,\n  \"balsamic vinegar\":33,\n  \"lime\":11,\n  \"pecans\":4,\n  \"peanut butter\":62,\n  \"cumin\":38,\n  \"green onions\":336,\n  \"canned chicken broth\":3,\n  \"banana\":3,\n  \"skim milk\":2,\n  \"beets\":2,\n  \"barley\":9,\n  \"kidney beans\":5,\n  \"shortening\":8,\n  \"parmesan cheese\":7,\n  \"carrots\":146,\n  \"mushrooms\":146,\n  \"sour cream\":19,\n  \"yogurt\":5,\n  \"brussels sprouts\":2,\n  \"cream cheese\":5,\n  \"tofu\":53,\n  \"cherry tomatoes\":19,\n  \"salsa\":5,\n  \"parsley\":59,\n  \"rice vinegar\":107,\n  \"molasses\":50,\n  \"beer\":23,\n  \"chili powder\":58,\n  \"spaghetti\":13,\n  \"raisins\":8,\n  \"red wine vinegar\":87,\n  \"chives\":8,\n  \"garam masala\":8,\n  \"cornstarch\":1167,\n  \"orange\":13,\n  \"dried basil\":15,\n  \"catsup\":90,\n  \"radishes\":10,\n  \"ground beef\":44,\n  \"adzuki beans\":3,\n  \"romaine lettuce\":5,\n  \"sweet red pepper\":12,\n  \"cumin seeds\":2,\n  \"barbecue sauce\":10,\n  \"red pepper flakes\":37,\n  \"almonds\":9,\n  \"eggs\":171,\n  \"margarine\":26,\n  \"fresh ginger\":100,\n  \"kale\":2,\n  \"garlic powder\":214,\n  \"dried thyme\":23,\n  \"cream of mushroom soup\":10,\n  \"whole wheat flour\":15,\n  \"salt\":1292,\n  \"fresh basil\":9,\n  \"bean sprouts\":98,\n  \"shiitake mushrooms\":24,\n  \"potatoes\":32,\n  \"celery seeds\":3,\n  \"allspice\":23,\n  \"dijon mustard\":64,\n  \"broccoli\":34,\n  \"chicken legs\":8,\n  \"yellow mustard\":3,\n  \"white rice\":4,\n  \"maple syrup\":17,\n  \"sweet potatoes\":6,\n  \"egg whites\":14,\n  \"worcestershire sauce\":133,\n  \"brown sugar\":335,\n  \"lime juice\":62,\n  \"dry mustard\":102,\n  \"raw shrimp\":12,\n  \"white flour\":2,\n  \"mayonnaise\":20,\n  \"chicken wings\":52,\n  \"fresh thyme\":5,\n  \"olive oil\":164,\n  \"vegetable oil\":429,\n  \"curry powder\":73,\n  \"flour\":127,\n  \"vinegar\":270,\n  \"white wine\":46,\n  \"sherry\":531,\n  \"oats\":3,\n  \"whipping cream\":3,\n  \"chicken breast\":85,\n  \"sugar\":1198},\n \"cheddar cheese\":\n {\"canned beef broth\":2,\n  \"red wine\":2,\n  \"elbow macaroni\":12,\n  \"canned tomato sauce\":3,\n  \"bacon\":45,\n  \"cayenne pepper\":27,\n  \"lemon juice\":34,\n  \"honey\":7,\n  \"swiss cheese\":17,\n  \"paprika\":53,\n  \"ground cinnamon\":5,\n  \"bread\":12,\n  \"yeast\":14,\n  \"egg yolks\":12,\n  \"green pepper\":74,\n  \"baking powder\":42,\n  \"cinnamon\":12,\n  \"avocado\":16,\n  \"corn tortilla\":5,\n  \"tomatoes\":62,\n  \"white pepper\":15,\n  \"brown rice\":4,\n  \"black beans\":7,\n  \"pinto beans\":11,\n  \"fresh garlic\":3,\n  \"cauliflower\":8,\n  \"butter\":207,\n  \"frozen peas\":7,\n  \"salmon\":4,\n  \"orange juice\":2,\n  \"black pepper\":45,\n  \"lemon\":2,\n  \"tumeric\":4,\n  \"black olives\":19,\n  \"buttermilk\":13,\n  \"dried oregano\":14,\n  \"eggplant\":6,\n  \"capers\":3,\n  \"cornmeal\":25,\n  \"cloves\":3,\n  \"oil\":32,\n  \"chicken\":26,\n  \"blue cheese\":2,\n  \"cucumber\":3,\n  \"flour tortilla\":6,\n  \"canola oil\":4,\n  \"celery\":46,\n  \"alfalfa sprouts\":5,\n  \"fresh cilantro\":10,\n  \"nutmeg\":18,\n  \"walnuts\":9,\n  \"bread crumbs\":27,\n  \"baking soda\":11,\n  \"tabasco sauce\":12,\n  \"pecans\":5,\n  \"peanut butter\":2,\n  \"green onions\":65,\n  \"frozen corn\":5,\n  \"cumin\":33,\n  \"skim milk\":7,\n  \"beets\":2,\n  \"barley\":3,\n  \"kidney beans\":17,\n  \"shortening\":10,\n  \"applesauce\":2,\n  \"parmesan cheese\":36,\n  \"carrots\":17,\n  \"mushrooms\":45,\n  \"sour cream\":132,\n  \"yogurt\":6,\n  \"brussels sprouts\":2,\n  \"tofu\":3,\n  \"cream cheese\":33,\n  \"frozen french fries\":2,\n  \"salsa\":24,\n  \"parsley\":48,\n  \"chili powder\":88,\n  \"beer\":7,\n  \"spaghetti\":10,\n  \"raisins\":2,\n  \"red wine vinegar\":2,\n  \"chives\":12,\n  \"cornstarch\":10,\n  \"dried basil\":6,\n  \"radishes\":2,\n  \"catsup\":5,\n  \"ground beef\":97,\n  \"romaine lettuce\":2,\n  \"mozzarella cheese\":25,\n  \"sweet red pepper\":6,\n  \"canned tuna\":2,\n  \"cumin seeds\":2,\n  \"barbecue sauce\":5,\n  \"red pepper flakes\":2,\n  \"almonds\":3,\n  \"eggs\":218,\n  \"margarine\":57,\n  \"kale\":2,\n  \"garlic powder\":43,\n  \"dried thyme\":4,\n  \"cream of mushroom soup\":33,\n  \"vanilla\":2,\n  \"whole wheat flour\":6,\n  \"salt\":392,\n  \"potatoes\":48,\n  \"frozen spinach\":7,\n  \"allspice\":8,\n  \"dijon mustard\":11,\n  \"soy sauce\":6,\n  \"broccoli\":21,\n  \"apples\":7,\n  \"sweet potatoes\":2,\n  \"shell macaroni\":3,\n  \"egg whites\":10,\n  \"worcestershire sauce\":56,\n  \"brown sugar\":10,\n  \"lime juice\":4,\n  \"dry mustard\":56,\n  \"mayonnaise\":56,\n  \"vegetable oil\":42,\n  \"olive oil\":36,\n  \"flour\":150,\n  \"curry powder\":17,\n  \"vinegar\":8,\n  \"white wine\":4,\n  \"sherry\":6,\n  \"oats\":2,\n  \"whipping cream\":8,\n  \"chicken breast\":6,\n  \"sugar\":67},\n \"cornish game hens\":\n {\"cream cheese\":2,\n  \"bacon\":2,\n  \"hoisin sauce\":4,\n  \"parsley\":2,\n  \"cayenne pepper\":2,\n  \"cloves\":2,\n  \"honey\":7,\n  \"oil\":2,\n  \"lemon juice\":11,\n  \"paprika\":4,\n  \"allspice\":2,\n  \"dijon mustard\":3,\n  \"ground cinnamon\":2,\n  \"soy sauce\":7,\n  \"raisins\":2,\n  \"celery\":3,\n  \"apples\":3,\n  \"maple syrup\":3,\n  \"green pepper\":2,\n  \"fresh cilantro\":2,\n  \"walnuts\":2,\n  \"nutmeg\":2,\n  \"cornstarch\":4,\n  \"orange\":3,\n  \"brown sugar\":7,\n  \"balsamic vinegar\":2,\n  \"ginger\":2,\n  \"cinnamon\":3,\n  \"lime juice\":4,\n  \"lime\":2,\n  \"tomatoes\":2,\n  \"green onions\":4,\n  \"vegetable oil\":3,\n  \"white pepper\":2,\n  \"barbecue sauce\":2,\n  \"olive oil\":6,\n  \"margarine\":3,\n  \"vinegar\":2,\n  \"butter\":12,\n  \"sherry\":3,\n  \"carrots\":2,\n  \"orange juice\":4,\n  \"mushrooms\":2,\n  \"black pepper\":4,\n  \"sugar\":5,\n  \"salt\":18,\n  \"lemon\":2},\n \"cucumber\":\n {\"red wine\":2,\n  \"elbow macaroni\":2,\n  \"bacon\":5,\n  \"cayenne pepper\":13,\n  \"lemon juice\":71,\n  \"honey\":6,\n  \"swiss cheese\":2,\n  \"paprika\":7,\n  \"bread\":4,\n  \"yeast\":2,\n  \"egg yolks\":3,\n  \"green pepper\":33,\n  \"saffron\":2,\n  \"sesame oil\":29,\n  \"cinnamon\":3,\n  \"pine nuts\":2,\n  \"avocado\":9,\n  \"cheddar cheese\":3,\n  \"tomatoes\":55,\n  \"white pepper\":12,\n  \"brown rice\":3,\n  \"black beans\":2,\n  \"cauliflower\":2,\n  \"frozen peas\":2,\n  \"butter\":13,\n  \"salmon\":3,\n  \"orange juice\":3,\n  \"lemon\":16,\n  \"black pepper\":21,\n  \"tumeric\":5,\n  \"black olives\":6,\n  \"buttermilk\":6,\n  \"hoisin sauce\":2,\n  \"dried oregano\":9,\n  \"capers\":7,\n  \"cloves\":2,\n  \"oil\":30,\n  \"chicken\":4,\n  \"blue cheese\":3,\n  \"canola oil\":4,\n  \"dried tarragon\":3,\n  \"celery\":17,\n  \"alfalfa sprouts\":6,\n  \"fresh peaches\":2,\n  \"fresh cilantro\":9,\n  \"nutmeg\":4,\n  \"baking soda\":2,\n  \"bread crumbs\":4,\n  \"tabasco sauce\":16,\n  \"ginger\":6,\n  \"balsamic vinegar\":5,\n  \"lime\":10,\n  \"peanut butter\":5,\n  \"green onions\":43,\n  \"cumin\":12,\n  \"beets\":3,\n  \"banana\":2,\n  \"carrots\":27,\n  \"mushrooms\":8,\n  \"sour cream\":35,\n  \"yogurt\":12,\n  \"cream cheese\":7,\n  \"cherry tomatoes\":10,\n  \"salsa\":2,\n  \"parsley\":30,\n  \"green olives\":2,\n  \"rice vinegar\":20,\n  \"chili powder\":8,\n  \"spaghetti\":6,\n  \"raisins\":2,\n  \"red wine vinegar\":26,\n  \"garam masala\":2,\n  \"chives\":7,\n  \"cornstarch\":11,\n  \"dried basil\":5,\n  \"orange\":3,\n  \"radishes\":22,\n  \"catsup\":2,\n  \"ground beef\":3,\n  \"mozzarella cheese\":2,\n  \"romaine lettuce\":5,\n  \"sweet red pepper\":4,\n  \"barbecue sauce\":2,\n  \"red pepper flakes\":5,\n  \"eggs\":11,\n  \"margarine\":2,\n  \"fresh ginger\":6,\n  \"garlic powder\":10,\n  \"dried thyme\":2,\n  \"cream of mushroom soup\":2,\n  \"whole wheat flour\":4,\n  \"salt\":223,\n  \"fresh basil\":2,\n  \"bean sprouts\":14,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":10,\n  \"celery seeds\":2,\n  \"allspice\":4,\n  \"dijon mustard\":12,\n  \"soy sauce\":33,\n  \"apples\":2,\n  \"maple syrup\":3,\n  \"sweet potatoes\":2,\n  \"worcestershire sauce\":11,\n  \"brown sugar\":10,\n  \"lime juice\":15,\n  \"dry mustard\":10,\n  \"mayonnaise\":37,\n  \"chicken wings\":2,\n  \"fresh thyme\":2,\n  \"vegetable oil\":28,\n  \"olive oil\":85,\n  \"flour\":4,\n  \"curry powder\":10,\n  \"vinegar\":41,\n  \"white wine\":4,\n  \"sherry\":5,\n  \"whipping cream\":2,\n  \"chicken breast\":2,\n  \"sugar\":106},\n \"barbecue sauce\":\n {\"sour cream\":2,\n  \"elbow macaroni\":3,\n  \"red wine\":2,\n  \"cherry tomatoes\":2,\n  \"bacon\":6,\n  \"salsa\":2,\n  \"parsley\":6,\n  \"cayenne pepper\":5,\n  \"lemon juice\":7,\n  \"honey\":7,\n  \"swiss cheese\":2,\n  \"molasses\":3,\n  \"chili powder\":16,\n  \"paprika\":10,\n  \"bread\":2,\n  \"ground cinnamon\":2,\n  \"yeast\":4,\n  \"green pepper\":6,\n  \"dried basil\":2,\n  \"radishes\":2,\n  \"catsup\":3,\n  \"ground beef\":10,\n  \"cinnamon\":2,\n  \"cheddar cheese\":5,\n  \"mozzarella cheese\":4,\n  \"tomatoes\":2,\n  \"white pepper\":2,\n  \"red pepper flakes\":2,\n  \"eggs\":3,\n  \"black beans\":2,\n  \"butter\":6,\n  \"garlic powder\":11,\n  \"orange juice\":3,\n  \"lemon\":2,\n  \"black pepper\":8,\n  \"salt\":32,\n  \"potatoes\":3,\n  \"dried oregano\":2,\n  \"cornmeal\":3,\n  \"oil\":4,\n  \"chicken\":3,\n  \"cucumber\":2,\n  \"dijon mustard\":4,\n  \"soy sauce\":10,\n  \"celery\":2,\n  \"cornish game hens\":2,\n  \"walnuts\":2,\n  \"egg whites\":2,\n  \"bread crumbs\":2,\n  \"baking soda\":2,\n  \"worcestershire sauce\":12,\n  \"tabasco sauce\":2,\n  \"brown sugar\":17,\n  \"ginger\":2,\n  \"lime juice\":2,\n  \"dry mustard\":5,\n  \"mayonnaise\":3,\n  \"frozen corn\":2,\n  \"green onions\":4,\n  \"cumin\":3,\n  \"chicken wings\":4,\n  \"vegetable oil\":6,\n  \"olive oil\":6,\n  \"barley\":2,\n  \"flour\":8,\n  \"kidney beans\":4,\n  \"shortening\":2,\n  \"vinegar\":5,\n  \"white wine\":2,\n  \"carrots\":4,\n  \"sherry\":2,\n  \"mushrooms\":3,\n  \"sugar\":10},\n \"yogurt\":\n {\"red wine\":2,\n  \"cayenne pepper\":13,\n  \"lemon juice\":43,\n  \"honey\":18,\n  \"swiss cheese\":2,\n  \"paprika\":18,\n  \"ground cinnamon\":4,\n  \"yeast\":11,\n  \"egg yolks\":6,\n  \"green pepper\":3,\n  \"baking powder\":32,\n  \"saffron\":7,\n  \"sesame oil\":2,\n  \"cream of tartar\":2,\n  \"pine nuts\":5,\n  \"cinnamon\":18,\n  \"avocado\":2,\n  \"cheddar cheese\":6,\n  \"tomatoes\":15,\n  \"white pepper\":5,\n  \"cauliflower\":2,\n  \"butter\":38,\n  \"orange juice\":7,\n  \"black pepper\":10,\n  \"lemon\":6,\n  \"tumeric\":6,\n  \"black olives\":2,\n  \"buttermilk\":2,\n  \"powdered sugar\":3,\n  \"dried oregano\":5,\n  \"eggplant\":5,\n  \"capers\":2,\n  \"cornmeal\":3,\n  \"cloves\":19,\n  \"oil\":32,\n  \"chicken\":14,\n  \"blue cheese\":3,\n  \"cucumber\":12,\n  \"canola oil\":2,\n  \"flour tortilla\":2,\n  \"dried tarragon\":2,\n  \"strawberries\":9,\n  \"celery\":8,\n  \"fresh cilantro\":2,\n  \"nutmeg\":9,\n  \"walnuts\":5,\n  \"bread crumbs\":2,\n  \"baking soda\":27,\n  \"tabasco sauce\":2,\n  \"ginger\":29,\n  \"lime\":2,\n  \"pecans\":6,\n  \"peanut butter\":2,\n  \"cumin\":25,\n  \"green onions\":6,\n  \"beets\":2,\n  \"skim milk\":3,\n  \"banana\":7,\n  \"kidney beans\":2,\n  \"shortening\":2,\n  \"applesauce\":6,\n  \"parmesan cheese\":5,\n  \"carrots\":9,\n  \"mushrooms\":8,\n  \"sour cream\":10,\n  \"tofu\":2,\n  \"cream cheese\":2,\n  \"salsa\":3,\n  \"parsley\":20,\n  \"molasses\":3,\n  \"chili powder\":14,\n  \"beer\":2,\n  \"raisins\":11,\n  \"garam masala\":22,\n  \"chives\":3,\n  \"cornstarch\":10,\n  \"orange\":2,\n  \"radishes\":4,\n  \"ground beef\":2,\n  \"cumin seeds\":13,\n  \"eggs\":29,\n  \"almonds\":12,\n  \"margarine\":11,\n  \"fresh ginger\":3,\n  \"garlic powder\":5,\n  \"dried thyme\":2,\n  \"vanilla\":17,\n  \"whole wheat flour\":7,\n  \"salt\":150,\n  \"potatoes\":12,\n  \"almond extract\":2,\n  \"frozen spinach\":2,\n  \"allspice\":4,\n  \"dijon mustard\":6,\n  \"soy sauce\":5,\n  \"broccoli\":4,\n  \"apples\":9,\n  \"yellow mustard\":2,\n  \"maple syrup\":2,\n  \"egg whites\":6,\n  \"worcestershire sauce\":4,\n  \"brown sugar\":5,\n  \"lime juice\":9,\n  \"dry mustard\":4,\n  \"white flour\":4,\n  \"blueberries\":5,\n  \"mayonnaise\":15,\n  \"vegetable oil\":28,\n  \"olive oil\":25,\n  \"curry powder\":18,\n  \"flour\":44,\n  \"vinegar\":6,\n  \"white wine\":2,\n  \"sherry\":3,\n  \"oats\":4,\n  \"whipping cream\":2,\n  \"chicken breast\":6,\n  \"sugar\":69},\n \"elbow macaroni\":\n {\"sour cream\":3,\n  \"cream cheese\":2,\n  \"canned tomato sauce\":3,\n  \"bacon\":6,\n  \"parsley\":7,\n  \"cayenne pepper\":8,\n  \"lemon juice\":4,\n  \"honey\":2,\n  \"swiss cheese\":3,\n  \"chili powder\":14,\n  \"paprika\":9,\n  \"ground cinnamon\":2,\n  \"bread\":2,\n  \"red wine vinegar\":3,\n  \"egg yolks\":2,\n  \"chives\":2,\n  \"green pepper\":9,\n  \"cornstarch\":4,\n  \"dried basil\":6,\n  \"catsup\":2,\n  \"cinnamon\":2,\n  \"ground beef\":12,\n  \"cheddar cheese\":12,\n  \"mozzarella cheese\":3,\n  \"tomatoes\":4,\n  \"barbecue sauce\":3,\n  \"white pepper\":7,\n  \"eggs\":11,\n  \"black beans\":2,\n  \"margarine\":5,\n  \"butter\":17,\n  \"frozen peas\":2,\n  \"garlic powder\":4,\n  \"dried thyme\":2,\n  \"cream of mushroom soup\":7,\n  \"salt\":59,\n  \"black pepper\":9,\n  \"fresh basil\":2,\n  \"black olives\":2,\n  \"eggplant\":2,\n  \"dried oregano\":2,\n  \"capers\":2,\n  \"oil\":4,\n  \"chicken\":2,\n  \"cucumber\":2,\n  \"celery seeds\":2,\n  \"dijon mustard\":3,\n  \"celery\":7,\n  \"apples\":2,\n  \"alfalfa sprouts\":2,\n  \"nutmeg\":3,\n  \"bread crumbs\":5,\n  \"worcestershire sauce\":6,\n  \"tabasco sauce\":3,\n  \"brown sugar\":2,\n  \"dry mustard\":15,\n  \"mayonnaise\":4,\n  \"frozen corn\":4,\n  \"cumin\":5,\n  \"vegetable oil\":6,\n  \"olive oil\":17,\n  \"skim milk\":8,\n  \"curry powder\":2,\n  \"flour\":19,\n  \"kidney beans\":2,\n  \"vinegar\":5,\n  \"parmesan cheese\":11,\n  \"carrots\":7,\n  \"whipping cream\":4,\n  \"sugar\":6,\n  \"mushrooms\":5},\n \"raw shrimp\":\n {\"sour cream\":2,\n  \"cream cheese\":2,\n  \"parsley\":5,\n  \"lemon juice\":5,\n  \"swiss cheese\":2,\n  \"paprika\":5,\n  \"chili powder\":2,\n  \"red wine vinegar\":2,\n  \"green pepper\":5,\n  \"cornstarch\":9,\n  \"dried basil\":3,\n  \"saffron\":3,\n  \"sesame oil\":6,\n  \"tomatoes\":4,\n  \"sweet red pepper\":2,\n  \"white pepper\":2,\n  \"red pepper flakes\":3,\n  \"eggs\":5,\n  \"fresh ginger\":2,\n  \"butter\":15,\n  \"frozen peas\":3,\n  \"garlic powder\":5,\n  \"dried thyme\":2,\n  \"black pepper\":3,\n  \"salt\":28,\n  \"lemon\":2,\n  \"bean sprouts\":2,\n  \"black olives\":2,\n  \"dried oregano\":2,\n  \"eggplant\":2,\n  \"oil\":7,\n  \"dijon mustard\":3,\n  \"dried tarragon\":2,\n  \"soy sauce\":12,\n  \"celery\":2,\n  \"fresh cilantro\":2,\n  \"worcestershire sauce\":2,\n  \"tabasco sauce\":2,\n  \"ginger\":2,\n  \"lime juice\":3,\n  \"dry mustard\":2,\n  \"fresh rosemary\":2,\n  \"mayonnaise\":3,\n  \"green onions\":13,\n  \"fresh thyme\":2,\n  \"olive oil\":11,\n  \"vegetable oil\":6,\n  \"flour\":4,\n  \"curry powder\":2,\n  \"white wine\":3,\n  \"sherry\":5,\n  \"carrots\":2,\n  \"whipping cream\":2,\n  \"chicken breast\":2,\n  \"sugar\":12,\n  \"mushrooms\":2},\n \"coconut oil\":\n {\"cauliflower\":3,\n  \"potatoes\":4,\n  \"dried thyme\":2,\n  \"carrots\":2,\n  \"tomatoes\":2,\n  \"cumin seeds\":2,\n  \"baking soda\":2,\n  \"lemon juice\":2,\n  \"mushrooms\":2,\n  \"sugar\":2,\n  \"salt\":13},\n \"frozen peas\":\n {\"elbow macaroni\":2,\n  \"red wine\":3,\n  \"bacon\":8,\n  \"cayenne pepper\":8,\n  \"honey\":2,\n  \"lemon juice\":10,\n  \"paprika\":13,\n  \"ground cinnamon\":2,\n  \"bread\":2,\n  \"green pepper\":10,\n  \"baking powder\":4,\n  \"saffron\":3,\n  \"sesame oil\":5,\n  \"cinnamon\":2,\n  \"pine nuts\":2,\n  \"avocado\":2,\n  \"cheddar cheese\":7,\n  \"tomatoes\":19,\n  \"white pepper\":2,\n  \"brown rice\":4,\n  \"fresh garlic\":2,\n  \"cauliflower\":2,\n  \"butter\":25,\n  \"orange juice\":2,\n  \"black pepper\":18,\n  \"lemon\":4,\n  \"tumeric\":4,\n  \"black olives\":2,\n  \"buttermilk\":2,\n  \"dried oregano\":8,\n  \"eggplant\":2,\n  \"cloves\":2,\n  \"oil\":16,\n  \"chicken\":6,\n  \"cucumber\":2,\n  \"canola oil\":7,\n  \"celery\":16,\n  \"nutmeg\":5,\n  \"walnuts\":2,\n  \"fresh cilantro\":2,\n  \"baking soda\":3,\n  \"bread crumbs\":3,\n  \"tabasco sauce\":2,\n  \"ginger\":3,\n  \"balsamic vinegar\":4,\n  \"frozen corn\":7,\n  \"green onions\":16,\n  \"cumin\":4,\n  \"canned chicken broth\":3,\n  \"skim milk\":7,\n  \"barley\":3,\n  \"kidney beans\":2,\n  \"shortening\":5,\n  \"parmesan cheese\":15,\n  \"carrots\":45,\n  \"mushrooms\":18,\n  \"sour cream\":11,\n  \"tofu\":4,\n  \"cherry tomatoes\":3,\n  \"parsley\":10,\n  \"rice vinegar\":2,\n  \"chili powder\":5,\n  \"spaghetti\":4,\n  \"raisins\":3,\n  \"red wine vinegar\":6,\n  \"chives\":2,\n  \"garam masala\":4,\n  \"cornstarch\":15,\n  \"dried basil\":7,\n  \"catsup\":2,\n  \"radishes\":2,\n  \"ground beef\":3,\n  \"sweet red pepper\":3,\n  \"cumin seeds\":3,\n  \"red pepper flakes\":5,\n  \"eggs\":16,\n  \"margarine\":4,\n  \"fresh ginger\":2,\n  \"garlic powder\":12,\n  \"dried thyme\":10,\n  \"cream of mushroom soup\":6,\n  \"whole wheat flour\":3,\n  \"salt\":127,\n  \"fresh basil\":2,\n  \"bean sprouts\":3,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":34,\n  \"frozen spinach\":2,\n  \"dijon mustard\":5,\n  \"soy sauce\":15,\n  \"chicken legs\":3,\n  \"sweet potatoes\":2,\n  \"worcestershire sauce\":5,\n  \"dry mustard\":4,\n  \"raw shrimp\":3,\n  \"mayonnaise\":10,\n  \"chicken wings\":2,\n  \"olive oil\":26,\n  \"vegetable oil\":23,\n  \"flour\":22,\n  \"curry powder\":12,\n  \"vinegar\":2,\n  \"white wine\":2,\n  \"sherry\":3,\n  \"whipping cream\":6,\n  \"chicken breast\":2,\n  \"sugar\":14},\n \"rice vinegar\":\n {\"sour cream\":4,\n  \"tofu\":2,\n  \"cherry tomatoes\":4,\n  \"cayenne pepper\":5,\n  \"parsley\":3,\n  \"honey\":40,\n  \"lemon juice\":17,\n  \"molasses\":3,\n  \"paprika\":4,\n  \"spaghetti\":2,\n  \"red wine vinegar\":4,\n  \"garam masala\":2,\n  \"chives\":2,\n  \"green pepper\":3,\n  \"cornstarch\":32,\n  \"orange\":2,\n  \"dried basil\":3,\n  \"radishes\":4,\n  \"sesame oil\":66,\n  \"pine nuts\":2,\n  \"avocado\":2,\n  \"sweet red pepper\":2,\n  \"tomatoes\":4,\n  \"white pepper\":11,\n  \"brown rice\":4,\n  \"red pepper flakes\":13,\n  \"black beans\":2,\n  \"eggs\":10,\n  \"fresh ginger\":12,\n  \"frozen peas\":2,\n  \"butter\":7,\n  \"garlic powder\":3,\n  \"dried thyme\":2,\n  \"salmon\":4,\n  \"orange juice\":5,\n  \"lemon\":3,\n  \"salt\":112,\n  \"black pepper\":9,\n  \"fresh basil\":3,\n  \"bean sprouts\":7,\n  \"shiitake mushrooms\":4,\n  \"buttermilk\":2,\n  \"dried oregano\":3,\n  \"hoisin sauce\":5,\n  \"capers\":3,\n  \"cloves\":3,\n  \"oil\":14,\n  \"chicken\":3,\n  \"blue cheese\":3,\n  \"cucumber\":20,\n  \"canola oil\":6,\n  \"dried tarragon\":2,\n  \"dijon mustard\":15,\n  \"soy sauce\":107,\n  \"strawberries\":2,\n  \"celery\":8,\n  \"broccoli\":4,\n  \"white rice\":2,\n  \"maple syrup\":2,\n  \"alfalfa sprouts\":3,\n  \"nutmeg\":2,\n  \"fresh peaches\":3,\n  \"fresh cilantro\":15,\n  \"walnuts\":2,\n  \"egg whites\":2,\n  \"baking soda\":2,\n  \"bread crumbs\":2,\n  \"worcestershire sauce\":3,\n  \"tabasco sauce\":4,\n  \"brown sugar\":15,\n  \"balsamic vinegar\":6,\n  \"ginger\":10,\n  \"lime juice\":10,\n  \"dry mustard\":8,\n  \"lime\":2,\n  \"peanut butter\":10,\n  \"mayonnaise\":4,\n  \"cumin\":3,\n  \"green onions\":31,\n  \"vegetable oil\":38,\n  \"olive oil\":26,\n  \"beets\":2,\n  \"flour\":3,\n  \"curry powder\":4,\n  \"vinegar\":2,\n  \"white wine\":3,\n  \"sherry\":11,\n  \"carrots\":16,\n  \"mushrooms\":8,\n  \"sugar\":121},\n \"cayenne pepper\":\n {\"canned beef broth\":3,\n  \"elbow macaroni\":8,\n  \"red wine\":9,\n  \"bacon\":34,\n  \"brown onions\":3,\n  \"lemon juice\":137,\n  \"honey\":52,\n  \"swiss cheese\":12,\n  \"paprika\":270,\n  \"bread\":7,\n  \"ground cinnamon\":46,\n  \"yeast\":5,\n  \"egg yolks\":27,\n  \"green pepper\":62,\n  \"baking powder\":20,\n  \"saffron\":4,\n  \"sesame oil\":14,\n  \"cream of tartar\":4,\n  \"pine nuts\":5,\n  \"cinnamon\":58,\n  \"avocado\":6,\n  \"cheddar cheese\":27,\n  \"tomatoes\":100,\n  \"white pepper\":84,\n  \"brown rice\":5,\n  \"black beans\":23,\n  \"fresh garlic\":2,\n  \"pinto beans\":10,\n  \"cauliflower\":8,\n  \"frozen peas\":8,\n  \"butter\":218,\n  \"salmon\":3,\n  \"orange juice\":20,\n  \"black pepper\":250,\n  \"lemon\":34,\n  \"tumeric\":13,\n  \"black olives\":5,\n  \"buttermilk\":9,\n  \"hoisin sauce\":7,\n  \"eggplant\":15,\n  \"dried oregano\":69,\n  \"capers\":9,\n  \"cornmeal\":15,\n  \"cloves\":23,\n  \"oil\":76,\n  \"chicken\":29,\n  \"blue cheese\":4,\n  \"cucumber\":13,\n  \"dry rosemary\":2,\n  \"canola oil\":17,\n  \"dried tarragon\":7,\n  \"strawberries\":2,\n  \"celery\":70,\n  \"cornish game hens\":2,\n  \"alfalfa sprouts\":2,\n  \"fresh peaches\":2,\n  \"fresh cilantro\":19,\n  \"nutmeg\":56,\n  \"walnuts\":10,\n  \"baking soda\":9,\n  \"bread crumbs\":33,\n  \"corn syrup\":2,\n  \"tabasco sauce\":48,\n  \"ginger\":43,\n  \"balsamic vinegar\":7,\n  \"fresh rosemary\":3,\n  \"lime\":12,\n  \"pecans\":13,\n  \"peanut butter\":4,\n  \"frozen corn\":9,\n  \"green onions\":75,\n  \"cumin\":133,\n  \"canned chicken broth\":2,\n  \"banana\":4,\n  \"skim milk\":15,\n  \"barley\":2,\n  \"kidney beans\":15,\n  \"shortening\":5,\n  \"applesauce\":3,\n  \"parmesan cheese\":26,\n  \"carrots\":59,\n  \"mushrooms\":29,\n  \"sour cream\":42,\n  \"yogurt\":13,\n  \"brussels sprouts\":4,\n  \"tofu\":6,\n  \"cream cheese\":20,\n  \"cherry tomatoes\":3,\n  \"salsa\":14,\n  \"parsley\":85,\n  \"green olives\":2,\n  \"rice vinegar\":5,\n  \"molasses\":15,\n  \"chili powder\":182,\n  \"beer\":21,\n  \"raisins\":25,\n  \"red wine vinegar\":34,\n  \"chives\":8,\n  \"garam masala\":27,\n  \"cornstarch\":33,\n  \"dried basil\":24,\n  \"orange\":5,\n  \"catsup\":21,\n  \"ground beef\":38,\n  \"mozzarella cheese\":7,\n  \"romaine lettuce\":2,\n  \"sweet red pepper\":6,\n  \"cumin seeds\":38,\n  \"barbecue sauce\":5,\n  \"red pepper flakes\":8,\n  \"almonds\":4,\n  \"eggs\":86,\n  \"margarine\":37,\n  \"fresh ginger\":20,\n  \"garlic powder\":167,\n  \"dried thyme\":69,\n  \"cream of mushroom soup\":4,\n  \"vanilla\":2,\n  \"whole wheat flour\":8,\n  \"salt\":928,\n  \"fresh basil\":8,\n  \"bean sprouts\":5,\n  \"shiitake mushrooms\":3,\n  \"potatoes\":25,\n  \"frozen spinach\":6,\n  \"celery seeds\":6,\n  \"allspice\":41,\n  \"dijon mustard\":33,\n  \"soy sauce\":65,\n  \"broccoli\":2,\n  \"chicken legs\":2,\n  \"apples\":6,\n  \"maple syrup\":2,\n  \"yellow mustard\":6,\n  \"white rice\":6,\n  \"sweet potatoes\":5,\n  \"egg whites\":8,\n  \"worcestershire sauce\":124,\n  \"dried cilantro\":2,\n  \"brown sugar\":72,\n  \"lime juice\":32,\n  \"dry mustard\":75,\n  \"white flour\":4,\n  \"mayonnaise\":40,\n  \"chicken wings\":7,\n  \"vegetable oil\":149,\n  \"olive oil\":244,\n  \"fresh thyme\":6,\n  \"flour\":165,\n  \"curry powder\":56,\n  \"vinegar\":52,\n  \"white wine\":28,\n  \"sherry\":12,\n  \"oats\":2,\n  \"whipping cream\":17,\n  \"chicken breast\":8,\n  \"sugar\":136},\n \"green onions\":\n {\"red wine\":8,\n  \"bacon\":45,\n  \"brown onions\":2,\n  \"cayenne pepper\":75,\n  \"honey\":51,\n  \"lemon juice\":137,\n  \"swiss cheese\":21,\n  \"paprika\":54,\n  \"camembert cheese\":2,\n  \"bread\":4,\n  \"ground cinnamon\":14,\n  \"egg yolks\":9,\n  \"green pepper\":84,\n  \"baking powder\":16,\n  \"saffron\":4,\n  \"sesame oil\":121,\n  \"pine nuts\":8,\n  \"cinnamon\":11,\n  \"avocado\":17,\n  \"cheddar cheese\":65,\n  \"tomatoes\":121,\n  \"white pepper\":46,\n  \"brown rice\":13,\n  \"black beans\":19,\n  \"fresh garlic\":4,\n  \"pinto beans\":5,\n  \"cauliflower\":6,\n  \"butter\":166,\n  \"frozen peas\":16,\n  \"salmon\":4,\n  \"orange juice\":32,\n  \"black pepper\":113,\n  \"lemon\":28,\n  \"tumeric\":2,\n  \"black olives\":28,\n  \"buttermilk\":10,\n  \"eggplant\":6,\n  \"hoisin sauce\":26,\n  \"dried oregano\":28,\n  \"capers\":9,\n  \"cornmeal\":3,\n  \"cloves\":6,\n  \"oil\":102,\n  \"chicken\":42,\n  \"blue cheese\":3,\n  \"cucumber\":43,\n  \"dry rosemary\":2,\n  \"flour tortilla\":3,\n  \"canola oil\":13,\n  \"dried tarragon\":9,\n  \"strawberries\":2,\n  \"celery\":101,\n  \"cornish game hens\":4,\n  \"alfalfa sprouts\":5,\n  \"fresh peaches\":2,\n  \"nutmeg\":16,\n  \"walnuts\":8,\n  \"fresh cilantro\":43,\n  \"baking soda\":5,\n  \"bread crumbs\":18,\n  \"tabasco sauce\":29,\n  \"balsamic vinegar\":19,\n  \"ginger\":40,\n  \"fresh rosemary\":2,\n  \"lime\":11,\n  \"pecans\":8,\n  \"peanut butter\":12,\n  \"frozen corn\":3,\n  \"cumin\":36,\n  \"canned chicken broth\":5,\n  \"banana\":2,\n  \"skim milk\":11,\n  \"beets\":2,\n  \"barley\":4,\n  \"kidney beans\":6,\n  \"shortening\":4,\n  \"parmesan cheese\":55,\n  \"carrots\":77,\n  \"mushrooms\":109,\n  \"sour cream\":112,\n  \"yogurt\":6,\n  \"brussels sprouts\":3,\n  \"cream cheese\":53,\n  \"tofu\":18,\n  \"cherry tomatoes\":15,\n  \"salsa\":23,\n  \"parsley\":148,\n  \"green olives\":10,\n  \"rice vinegar\":31,\n  \"molasses\":4,\n  \"beer\":4,\n  \"chili powder\":66,\n  \"spaghetti\":14,\n  \"raisins\":10,\n  \"red wine vinegar\":36,\n  \"garam masala\":5,\n  \"chives\":9,\n  \"cornstarch\":186,\n  \"orange\":9,\n  \"dried basil\":27,\n  \"catsup\":10,\n  \"radishes\":13,\n  \"ground beef\":23,\n  \"romaine lettuce\":4,\n  \"mozzarella cheese\":13,\n  \"adzuki beans\":2,\n  \"sweet red pepper\":25,\n  \"cumin seeds\":8,\n  \"barbecue sauce\":4,\n  \"raw spinach\":2,\n  \"red pepper flakes\":18,\n  \"almonds\":9,\n  \"eggs\":149,\n  \"margarine\":32,\n  \"fresh ginger\":34,\n  \"garlic powder\":62,\n  \"dried thyme\":26,\n  \"cream of mushroom soup\":13,\n  \"salt\":622,\n  \"fresh basil\":14,\n  \"bean sprouts\":43,\n  \"shiitake mushrooms\":9,\n  \"potatoes\":45,\n  \"frozen spinach\":11,\n  \"celery seeds\":2,\n  \"allspice\":7,\n  \"dijon mustard\":39,\n  \"soy sauce\":336,\n  \"broccoli\":12,\n  \"chicken legs\":2,\n  \"apples\":4,\n  \"maple syrup\":3,\n  \"white rice\":5,\n  \"sweet potatoes\":5,\n  \"yellow mustard\":4,\n  \"egg whites\":18,\n  \"worcestershire sauce\":75,\n  \"dried cilantro\":2,\n  \"brown sugar\":46,\n  \"lime juice\":60,\n  \"dry mustard\":36,\n  \"raw shrimp\":13,\n  \"canned vegetable broth\":3,\n  \"white flour\":4,\n  \"mayonnaise\":107,\n  \"chicken wings\":5,\n  \"olive oil\":215,\n  \"vegetable oil\":180,\n  \"fresh thyme\":8,\n  \"curry powder\":43,\n  \"flour\":99,\n  \"vinegar\":50,\n  \"white wine\":40,\n  \"sherry\":32,\n  \"yam\":2,\n  \"whipping cream\":20,\n  \"chicken breast\":18,\n  \"sugar\":253},\n \"ginger\":\n {\"canned beef broth\":3,\n  \"red wine\":4,\n  \"bacon\":6,\n  \"brown onions\":2,\n  \"cayenne pepper\":43,\n  \"honey\":114,\n  \"lemon juice\":109,\n  \"paprika\":57,\n  \"ground cinnamon\":8,\n  \"yeast\":30,\n  \"egg yolks\":9,\n  \"green pepper\":21,\n  \"baking powder\":95,\n  \"saffron\":13,\n  \"sesame oil\":82,\n  \"cream of tartar\":9,\n  \"pine nuts\":4,\n  \"cinnamon\":394,\n  \"tomatoes\":40,\n  \"white pepper\":23,\n  \"brown rice\":3,\n  \"black beans\":6,\n  \"fresh garlic\":3,\n  \"cauliflower\":11,\n  \"frozen peas\":3,\n  \"butter\":134,\n  \"salmon\":2,\n  \"orange juice\":47,\n  \"black pepper\":74,\n  \"lemon\":27,\n  \"tumeric\":20,\n  \"buttermilk\":24,\n  \"powdered sugar\":7,\n  \"hoisin sauce\":14,\n  \"eggplant\":12,\n  \"capers\":2,\n  \"cornmeal\":11,\n  \"cloves\":196,\n  \"oil\":178,\n  \"chicken\":36,\n  \"cucumber\":6,\n  \"canola oil\":8,\n  \"dried tarragon\":2,\n  \"celery\":33,\n  \"cornish game hens\":2,\n  \"fresh peaches\":2,\n  \"fresh cilantro\":8,\n  \"nutmeg\":196,\n  \"walnuts\":19,\n  \"baking soda\":175,\n  \"bread crumbs\":16,\n  \"tabasco sauce\":8,\n  \"corn syrup\":7,\n  \"balsamic vinegar\":8,\n  \"lime\":11,\n  \"pecans\":7,\n  \"peanut butter\":16,\n  \"cumin\":68,\n  \"green onions\":40,\n  \"frozen corn\":2,\n  \"banana\":4,\n  \"skim milk\":12,\n  \"barley\":2,\n  \"kidney beans\":2,\n  \"shortening\":74,\n  \"parmesan cheese\":4,\n  \"applesauce\":16,\n  \"carrots\":52,\n  \"mushrooms\":35,\n  \"sour cream\":23,\n  \"yogurt\":29,\n  \"cream cheese\":14,\n  \"tofu\":12,\n  \"cherry tomatoes\":5,\n  \"parsley\":25,\n  \"green olives\":2,\n  \"rice vinegar\":10,\n  \"molasses\":162,\n  \"chili powder\":34,\n  \"beer\":6,\n  \"raisins\":76,\n  \"red wine vinegar\":21,\n  \"garam masala\":29,\n  \"chives\":5,\n  \"cornstarch\":145,\n  \"orange\":17,\n  \"catsup\":10,\n  \"radishes\":4,\n  \"ground beef\":10,\n  \"sweet red pepper\":2,\n  \"cumin seeds\":21,\n  \"barbecue sauce\":2,\n  \"red pepper flakes\":11,\n  \"eggs\":151,\n  \"almonds\":18,\n  \"margarine\":37,\n  \"kale\":3,\n  \"garlic powder\":63,\n  \"vanilla\":57,\n  \"whole wheat flour\":22,\n  \"salt\":677,\n  \"bean sprouts\":18,\n  \"potatoes\":17,\n  \"almond extract\":3,\n  \"frozen spinach\":3,\n  \"celery seeds\":3,\n  \"allspice\":93,\n  \"dijon mustard\":5,\n  \"soy sauce\":308,\n  \"broccoli\":5,\n  \"chicken legs\":3,\n  \"apples\":23,\n  \"sweet potatoes\":11,\n  \"maple syrup\":20,\n  \"egg whites\":28,\n  \"worcestershire sauce\":19,\n  \"dried cilantro\":3,\n  \"brown sugar\":187,\n  \"lime juice\":26,\n  \"dry mustard\":50,\n  \"raw shrimp\":2,\n  \"white flour\":10,\n  \"blueberries\":2,\n  \"mayonnaise\":6,\n  \"chicken wings\":6,\n  \"fresh thyme\":2,\n  \"olive oil\":47,\n  \"vegetable oil\":110,\n  \"flour\":217,\n  \"curry powder\":41,\n  \"vinegar\":106,\n  \"white wine\":14,\n  \"sherry\":48,\n  \"oats\":6,\n  \"whipping cream\":10,\n  \"chicken breast\":21,\n  \"sugar\":487},\n \"walnuts\":\n {\"red wine\":5,\n  \"bacon\":2,\n  \"cayenne pepper\":10,\n  \"lemon juice\":86,\n  \"honey\":61,\n  \"paprika\":6,\n  \"camembert cheese\":2,\n  \"bread\":3,\n  \"ground cinnamon\":67,\n  \"yeast\":13,\n  \"egg yolks\":8,\n  \"green pepper\":6,\n  \"baking powder\":237,\n  \"saffron\":3,\n  \"sesame oil\":5,\n  \"pine nuts\":8,\n  \"cinnamon\":199,\n  \"cream of tartar\":13,\n  \"cheddar cheese\":9,\n  \"tomatoes\":5,\n  \"white pepper\":2,\n  \"brown rice\":2,\n  \"frozen peas\":2,\n  \"butter\":282,\n  \"salmon\":2,\n  \"orange juice\":29,\n  \"black pepper\":16,\n  \"lemon\":12,\n  \"buttermilk\":28,\n  \"powdered sugar\":50,\n  \"hoisin sauce\":2,\n  \"eggplant\":2,\n  \"dried oregano\":5,\n  \"cornmeal\":6,\n  \"cloves\":35,\n  \"oil\":70,\n  \"chicken\":8,\n  \"blue cheese\":6,\n  \"canola oil\":11,\n  \"strawberries\":8,\n  \"celery\":33,\n  \"alfalfa sprouts\":2,\n  \"cornish game hens\":2,\n  \"nutmeg\":93,\n  \"baking soda\":238,\n  \"bread crumbs\":12,\n  \"tabasco sauce\":2,\n  \"corn syrup\":11,\n  \"balsamic vinegar\":7,\n  \"ginger\":19,\n  \"fresh rosemary\":3,\n  \"pecans\":14,\n  \"peanut butter\":6,\n  \"green onions\":8,\n  \"cumin\":10,\n  \"skim milk\":9,\n  \"banana\":15,\n  \"beets\":3,\n  \"shortening\":50,\n  \"parmesan cheese\":13,\n  \"applesauce\":13,\n  \"carrots\":25,\n  \"mushrooms\":13,\n  \"sour cream\":41,\n  \"yogurt\":5,\n  \"brussels sprouts\":2,\n  \"tofu\":6,\n  \"cream cheese\":49,\n  \"parsley\":27,\n  \"green olives\":2,\n  \"rice vinegar\":2,\n  \"molasses\":9,\n  \"chili powder\":4,\n  \"beer\":4,\n  \"spaghetti\":3,\n  \"raisins\":136,\n  \"red wine vinegar\":4,\n  \"chives\":5,\n  \"garam masala\":3,\n  \"cornstarch\":21,\n  \"orange\":20,\n  \"dried basil\":5,\n  \"radishes\":2,\n  \"ground beef\":2,\n  \"romaine lettuce\":2,\n  \"mozzarella cheese\":4,\n  \"barbecue sauce\":2,\n  \"red pepper flakes\":2,\n  \"eggs\":326,\n  \"almonds\":18,\n  \"margarine\":62,\n  \"fresh ginger\":3,\n  \"garlic powder\":9,\n  \"dried thyme\":6,\n  \"vanilla\":229,\n  \"whole wheat flour\":39,\n  \"salt\":521,\n  \"fresh basil\":2,\n  \"potatoes\":3,\n  \"almond extract\":15,\n  \"allspice\":24,\n  \"dijon mustard\":13,\n  \"soy sauce\":11,\n  \"broccoli\":4,\n  \"apples\":41,\n  \"sweet potatoes\":4,\n  \"maple syrup\":9,\n  \"egg whites\":36,\n  \"worcestershire sauce\":7,\n  \"brown sugar\":149,\n  \"lime juice\":6,\n  \"dry mustard\":5,\n  \"white flour\":5,\n  \"blueberries\":3,\n  \"mayonnaise\":21,\n  \"fresh thyme\":2,\n  \"vegetable oil\":70,\n  \"olive oil\":58,\n  \"flour\":327,\n  \"curry powder\":6,\n  \"vinegar\":13,\n  \"sherry\":6,\n  \"oats\":7,\n  \"whipping cream\":16,\n  \"sugar\":527},\n \"frozen corn\":\n {\"sour cream\":4,\n  \"elbow macaroni\":4,\n  \"bacon\":2,\n  \"salsa\":4,\n  \"cayenne pepper\":9,\n  \"parsley\":3,\n  \"honey\":2,\n  \"lemon juice\":2,\n  \"beer\":2,\n  \"chili powder\":17,\n  \"paprika\":4,\n  \"ground cinnamon\":2,\n  \"garam masala\":2,\n  \"green pepper\":9,\n  \"baking powder\":4,\n  \"cornstarch\":3,\n  \"dried basil\":4,\n  \"cinnamon\":3,\n  \"ground beef\":3,\n  \"cheddar cheese\":5,\n  \"mozzarella cheese\":2,\n  \"tomatoes\":9,\n  \"sweet red pepper\":2,\n  \"barbecue sauce\":2,\n  \"white pepper\":2,\n  \"red pepper flakes\":3,\n  \"black beans\":6,\n  \"eggs\":8,\n  \"pinto beans\":3,\n  \"margarine\":6,\n  \"frozen peas\":7,\n  \"butter\":10,\n  \"garlic powder\":3,\n  \"dried thyme\":4,\n  \"cream of mushroom soup\":2,\n  \"whole wheat flour\":2,\n  \"black pepper\":12,\n  \"salt\":42,\n  \"fresh basil\":2,\n  \"potatoes\":10,\n  \"eggplant\":2,\n  \"cornmeal\":3,\n  \"cloves\":2,\n  \"oil\":3,\n  \"chicken\":3,\n  \"canola oil\":3,\n  \"dijon mustard\":3,\n  \"celery\":3,\n  \"maple syrup\":2,\n  \"fresh cilantro\":3,\n  \"egg whites\":2,\n  \"bread crumbs\":4,\n  \"worcestershire sauce\":8,\n  \"tabasco sauce\":2,\n  \"brown sugar\":2,\n  \"balsamic vinegar\":2,\n  \"ginger\":2,\n  \"lime juice\":7,\n  \"dry mustard\":3,\n  \"mayonnaise\":2,\n  \"green onions\":3,\n  \"cumin\":13,\n  \"vegetable oil\":9,\n  \"olive oil\":13,\n  \"skim milk\":5,\n  \"curry powder\":3,\n  \"barley\":2,\n  \"flour\":7,\n  \"vinegar\":2,\n  \"carrots\":12,\n  \"whipping cream\":4,\n  \"chicken breast\":2,\n  \"mushrooms\":3,\n  \"sugar\":5},\n \"mozzarella cheese\":\n {\"red wine\":4,\n  \"elbow macaroni\":3,\n  \"bacon\":3,\n  \"brown onions\":2,\n  \"cayenne pepper\":7,\n  \"lemon juice\":11,\n  \"honey\":4,\n  \"swiss cheese\":6,\n  \"zucchini squash\":2,\n  \"paprika\":11,\n  \"bread\":2,\n  \"yeast\":8,\n  \"egg yolks\":2,\n  \"green pepper\":31,\n  \"baking powder\":6,\n  \"pine nuts\":4,\n  \"cheddar cheese\":25,\n  \"tomatoes\":36,\n  \"white pepper\":9,\n  \"brown rice\":3,\n  \"black beans\":2,\n  \"pinto beans\":2,\n  \"fresh garlic\":4,\n  \"butter\":47,\n  \"black pepper\":41,\n  \"lemon\":3,\n  \"black olives\":5,\n  \"dried oregano\":37,\n  \"eggplant\":34,\n  \"hoisin sauce\":2,\n  \"capers\":6,\n  \"cornmeal\":5,\n  \"cloves\":2,\n  \"oil\":29,\n  \"chicken\":6,\n  \"cucumber\":2,\n  \"flour tortilla\":3,\n  \"canola oil\":2,\n  \"celery\":6,\n  \"nutmeg\":21,\n  \"walnuts\":4,\n  \"fresh cilantro\":3,\n  \"bread crumbs\":21,\n  \"baking soda\":3,\n  \"tabasco sauce\":5,\n  \"balsamic vinegar\":8,\n  \"pecans\":2,\n  \"green onions\":13,\n  \"cumin\":8,\n  \"frozen corn\":2,\n  \"skim milk\":7,\n  \"shortening\":2,\n  \"parmesan cheese\":170,\n  \"carrots\":9,\n  \"mushrooms\":40,\n  \"sour cream\":17,\n  \"tofu\":2,\n  \"cream cheese\":10,\n  \"cherry tomatoes\":4,\n  \"salsa\":7,\n  \"parsley\":40,\n  \"green olives\":2,\n  \"chili powder\":3,\n  \"spaghetti\":12,\n  \"red wine vinegar\":7,\n  \"chives\":2,\n  \"cornstarch\":12,\n  \"dried basil\":36,\n  \"radishes\":2,\n  \"catsup\":2,\n  \"ground beef\":50,\n  \"cumin seeds\":2,\n  \"barbecue sauce\":4,\n  \"red pepper flakes\":8,\n  \"eggs\":94,\n  \"margarine\":18,\n  \"garlic powder\":36,\n  \"dried thyme\":6,\n  \"cream of mushroom soup\":4,\n  \"whole wheat flour\":4,\n  \"salt\":209,\n  \"fresh basil\":18,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":4,\n  \"frozen spinach\":11,\n  \"dijon mustard\":5,\n  \"broccoli\":5,\n  \"egg whites\":12,\n  \"worcestershire sauce\":8,\n  \"dry mustard\":3,\n  \"mayonnaise\":14,\n  \"vegetable oil\":16,\n  \"olive oil\":120,\n  \"fresh thyme\":2,\n  \"flour\":45,\n  \"white wine\":7,\n  \"sherry\":4,\n  \"whipping cream\":4,\n  \"chicken breast\":7,\n  \"sugar\":40},\n \"zucchini squash\":\n {\"potatoes\":2,\n  \"eggplant\":2,\n  \"parsley\":2,\n  \"honey\":2,\n  \"lemon juice\":2,\n  \"chicken\":2,\n  \"paprika\":2,\n  \"chili powder\":2,\n  \"dijon mustard\":2,\n  \"green pepper\":3,\n  \"egg whites\":2,\n  \"baking powder\":2,\n  \"dried basil\":2,\n  \"pine nuts\":2,\n  \"cinnamon\":2,\n  \"mozzarella cheese\":2,\n  \"cumin seeds\":2,\n  \"olive oil\":3,\n  \"skim milk\":2,\n  \"brown rice\":2,\n  \"curry powder\":2,\n  \"eggs\":2,\n  \"cauliflower\":2,\n  \"shortening\":2,\n  \"butter\":3,\n  \"garlic powder\":2,\n  \"white wine\":2,\n  \"parmesan cheese\":3,\n  \"carrots\":2,\n  \"whole wheat flour\":2,\n  \"salt\":10,\n  \"black pepper\":2},\n \"sweet red onions\":\n {\"olive oil\":3, \"balsamic vinegar\":2, \"dry mustard\":2, \"carrots\":2},\n \"spaghetti\":\n {\"sour cream\":5,\n  \"red wine\":3,\n  \"tofu\":2,\n  \"canned tomato sauce\":4,\n  \"bacon\":5,\n  \"parsley\":31,\n  \"green olives\":3,\n  \"honey\":3,\n  \"rice vinegar\":2,\n  \"lemon juice\":9,\n  \"swiss cheese\":3,\n  \"chili powder\":12,\n  \"paprika\":4,\n  \"raisins\":3,\n  \"red wine vinegar\":5,\n  \"chives\":2,\n  \"egg yolks\":2,\n  \"green pepper\":13,\n  \"cornstarch\":8,\n  \"orange\":2,\n  \"dried basil\":8,\n  \"radishes\":2,\n  \"sesame oil\":10,\n  \"pine nuts\":4,\n  \"ground beef\":17,\n  \"cinnamon\":6,\n  \"cheddar cheese\":10,\n  \"mozzarella cheese\":12,\n  \"tomatoes\":23,\n  \"cumin seeds\":3,\n  \"white pepper\":2,\n  \"red pepper flakes\":7,\n  \"almonds\":2,\n  \"eggs\":22,\n  \"margarine\":3,\n  \"fresh ginger\":2,\n  \"butter\":24,\n  \"frozen peas\":4,\n  \"garlic powder\":9,\n  \"dried thyme\":2,\n  \"cream of mushroom soup\":3,\n  \"orange juice\":2,\n  \"salt\":94,\n  \"black pepper\":15,\n  \"lemon\":5,\n  \"fresh basil\":8,\n  \"bean sprouts\":3,\n  \"black olives\":3,\n  \"shiitake mushrooms\":2,\n  \"eggplant\":3,\n  \"dried oregano\":15,\n  \"capers\":5,\n  \"cloves\":2,\n  \"oil\":11,\n  \"chicken\":10,\n  \"cucumber\":6,\n  \"allspice\":4,\n  \"soy sauce\":13,\n  \"celery\":16,\n  \"broccoli\":4,\n  \"walnuts\":3,\n  \"nutmeg\":7,\n  \"egg whites\":3,\n  \"bread crumbs\":2,\n  \"worcestershire sauce\":8,\n  \"tabasco sauce\":2,\n  \"brown sugar\":2,\n  \"balsamic vinegar\":2,\n  \"lime juice\":2,\n  \"dry mustard\":3,\n  \"fresh rosemary\":2,\n  \"peanut butter\":4,\n  \"cumin\":3,\n  \"green onions\":14,\n  \"fresh thyme\":2,\n  \"vegetable oil\":16,\n  \"olive oil\":77,\n  \"flour\":16,\n  \"shortening\":2,\n  \"white wine\":7,\n  \"parmesan cheese\":34,\n  \"carrots\":7,\n  \"sherry\":6,\n  \"whipping cream\":4,\n  \"chicken breast\":2,\n  \"mushrooms\":18,\n  \"sugar\":18},\n \"sherry\":\n {\"red wine\":5,\n  \"bacon\":11,\n  \"cayenne pepper\":12,\n  \"honey\":41,\n  \"lemon juice\":43,\n  \"swiss cheese\":3,\n  \"paprika\":50,\n  \"bread\":3,\n  \"egg yolks\":28,\n  \"green pepper\":16,\n  \"baking powder\":7,\n  \"sesame oil\":89,\n  \"cinnamon\":17,\n  \"cream of tartar\":3,\n  \"cheddar cheese\":6,\n  \"tomatoes\":22,\n  \"white pepper\":22,\n  \"brown rice\":2,\n  \"black beans\":3,\n  \"fresh garlic\":2,\n  \"cauliflower\":4,\n  \"frozen peas\":3,\n  \"butter\":153,\n  \"orange juice\":17,\n  \"black pepper\":20,\n  \"lemon\":11,\n  \"black olives\":2,\n  \"powdered sugar\":3,\n  \"eggplant\":4,\n  \"dried oregano\":6,\n  \"hoisin sauce\":26,\n  \"capers\":2,\n  \"cloves\":7,\n  \"oil\":435,\n  \"chicken\":28,\n  \"blue cheese\":2,\n  \"cucumber\":5,\n  \"canola oil\":4,\n  \"dried tarragon\":4,\n  \"strawberries\":6,\n  \"celery\":43,\n  \"cornish game hens\":3,\n  \"walnuts\":6,\n  \"nutmeg\":20,\n  \"bread crumbs\":14,\n  \"baking soda\":5,\n  \"tabasco sauce\":8,\n  \"balsamic vinegar\":3,\n  \"ginger\":48,\n  \"lime\":2,\n  \"pecans\":3,\n  \"peanut butter\":4,\n  \"green onions\":32,\n  \"cumin\":5,\n  \"beets\":2,\n  \"skim milk\":4,\n  \"barley\":2,\n  \"shortening\":3,\n  \"applesauce\":2,\n  \"parmesan cheese\":26,\n  \"carrots\":35,\n  \"mushrooms\":64,\n  \"sour cream\":22,\n  \"yogurt\":3,\n  \"tofu\":3,\n  \"cream cheese\":5,\n  \"salsa\":2,\n  \"parsley\":27,\n  \"green olives\":3,\n  \"rice vinegar\":11,\n  \"molasses\":2,\n  \"chili powder\":6,\n  \"spaghetti\":6,\n  \"raisins\":8,\n  \"red wine vinegar\":9,\n  \"chives\":3,\n  \"cornstarch\":378,\n  \"orange\":7,\n  \"dried basil\":7,\n  \"catsup\":17,\n  \"radishes\":3,\n  \"ground beef\":4,\n  \"mozzarella cheese\":4,\n  \"sweet red pepper\":2,\n  \"barbecue sauce\":2,\n  \"red pepper flakes\":3,\n  \"almonds\":8,\n  \"eggs\":56,\n  \"margarine\":14,\n  \"fresh ginger\":17,\n  \"garlic powder\":20,\n  \"kale\":2,\n  \"dried thyme\":10,\n  \"cream of mushroom soup\":3,\n  \"vanilla\":5,\n  \"whole wheat flour\":2,\n  \"salt\":592,\n  \"bean sprouts\":22,\n  \"shiitake mushrooms\":4,\n  \"potatoes\":7,\n  \"allspice\":3,\n  \"dijon mustard\":5,\n  \"soy sauce\":531,\n  \"broccoli\":6,\n  \"apples\":4,\n  \"chicken legs\":2,\n  \"white rice\":2,\n  \"sweet potatoes\":4,\n  \"maple syrup\":6,\n  \"egg whites\":19,\n  \"worcestershire sauce\":26,\n  \"brown sugar\":42,\n  \"lime juice\":2,\n  \"dry mustard\":20,\n  \"raw shrimp\":5,\n  \"mayonnaise\":7,\n  \"chicken wings\":5,\n  \"olive oil\":35,\n  \"fresh thyme\":2,\n  \"vegetable oil\":34,\n  \"flour\":137,\n  \"curry powder\":16,\n  \"vinegar\":50,\n  \"white wine\":8,\n  \"yam\":2,\n  \"whipping cream\":13,\n  \"chicken breast\":34,\n  \"sugar\":401},\n \"tofu\":\n {\"canned tomato sauce\":3,\n  \"cayenne pepper\":6,\n  \"lemon juice\":29,\n  \"honey\":13,\n  \"paprika\":6,\n  \"bread\":2,\n  \"green pepper\":6,\n  \"baking powder\":5,\n  \"sesame oil\":21,\n  \"cinnamon\":4,\n  \"cream of tartar\":3,\n  \"cheddar cheese\":3,\n  \"tomatoes\":13,\n  \"brown rice\":6,\n  \"pinto beans\":2,\n  \"cauliflower\":4,\n  \"frozen peas\":4,\n  \"butter\":6,\n  \"orange juice\":3,\n  \"black pepper\":21,\n  \"lemon\":2,\n  \"tumeric\":2,\n  \"black olives\":2,\n  \"eggplant\":3,\n  \"capers\":2,\n  \"cornmeal\":2,\n  \"cloves\":6,\n  \"oil\":41,\n  \"canola oil\":7,\n  \"strawberries\":2,\n  \"celery\":11,\n  \"nutmeg\":2,\n  \"walnuts\":6,\n  \"bread crumbs\":12,\n  \"baking soda\":4,\n  \"ginger\":12,\n  \"balsamic vinegar\":3,\n  \"pecans\":2,\n  \"peanut butter\":13,\n  \"cumin\":10,\n  \"green onions\":18,\n  \"skim milk\":3,\n  \"kidney beans\":3,\n  \"shortening\":2,\n  \"parmesan cheese\":3,\n  \"applesauce\":2,\n  \"carrots\":12,\n  \"mushrooms\":27,\n  \"yogurt\":2,\n  \"salsa\":4,\n  \"parsley\":11,\n  \"rice vinegar\":2,\n  \"chili powder\":14,\n  \"spaghetti\":2,\n  \"raisins\":6,\n  \"red wine vinegar\":2,\n  \"garam masala\":2,\n  \"chives\":3,\n  \"cornstarch\":24,\n  \"orange\":2,\n  \"dried basil\":2,\n  \"romaine lettuce\":2,\n  \"mozzarella cheese\":2,\n  \"sweet red pepper\":2,\n  \"red pepper flakes\":2,\n  \"eggs\":10,\n  \"almonds\":2,\n  \"margarine\":4,\n  \"fresh ginger\":6,\n  \"garlic powder\":16,\n  \"vanilla\":8,\n  \"whole wheat flour\":3,\n  \"salt\":57,\n  \"fresh basil\":2,\n  \"bean sprouts\":5,\n  \"potatoes\":3,\n  \"almond extract\":2,\n  \"frozen spinach\":2,\n  \"allspice\":3,\n  \"dijon mustard\":3,\n  \"soy sauce\":53,\n  \"broccoli\":8,\n  \"maple syrup\":4,\n  \"sweet potatoes\":2,\n  \"egg whites\":4,\n  \"worcestershire sauce\":6,\n  \"brown sugar\":4,\n  \"lime juice\":3,\n  \"white flour\":3,\n  \"mayonnaise\":3,\n  \"vegetable oil\":25,\n  \"olive oil\":21,\n  \"curry powder\":6,\n  \"flour\":15,\n  \"vinegar\":7,\n  \"white wine\":2,\n  \"sherry\":3,\n  \"oats\":2,\n  \"sugar\":32},\n \"dried bay leaf\":\n {\"olive oil\":2,\n  \"dried basil\":2,\n  \"dried oregano\":2,\n  \"eggplant\":2,\n  \"whipping cream\":2,\n  \"salt\":2,\n  \"mushrooms\":2,\n  \"black pepper\":2},\n \"bean sprouts\":\n {\"sour cream\":2,\n  \"tofu\":5,\n  \"bacon\":2,\n  \"cayenne pepper\":5,\n  \"lemon juice\":7,\n  \"rice vinegar\":7,\n  \"molasses\":2,\n  \"paprika\":11,\n  \"chili powder\":4,\n  \"spaghetti\":3,\n  \"red wine vinegar\":2,\n  \"yeast\":2,\n  \"chives\":4,\n  \"green pepper\":9,\n  \"baking powder\":2,\n  \"cornstarch\":56,\n  \"dried basil\":2,\n  \"catsup\":2,\n  \"sesame oil\":28,\n  \"radishes\":4,\n  \"ground beef\":9,\n  \"avocado\":2,\n  \"romaine lettuce\":2,\n  \"tomatoes\":4,\n  \"sweet red pepper\":2,\n  \"white pepper\":4,\n  \"brown rice\":4,\n  \"raw spinach\":2,\n  \"red pepper flakes\":4,\n  \"eggs\":30,\n  \"margarine\":2,\n  \"fresh ginger\":7,\n  \"cauliflower\":5,\n  \"frozen peas\":3,\n  \"butter\":4,\n  \"garlic powder\":2,\n  \"cream of mushroom soup\":3,\n  \"orange juice\":2,\n  \"black pepper\":10,\n  \"lemon\":3,\n  \"salt\":83,\n  \"fresh basil\":2,\n  \"tumeric\":2,\n  \"shiitake mushrooms\":4,\n  \"potatoes\":5,\n  \"hoisin sauce\":8,\n  \"oil\":71,\n  \"chicken\":10,\n  \"cucumber\":14,\n  \"soy sauce\":98,\n  \"celery\":21,\n  \"broccoli\":3,\n  \"white rice\":2,\n  \"alfalfa sprouts\":2,\n  \"fresh cilantro\":2,\n  \"nutmeg\":2,\n  \"egg whites\":3,\n  \"baking soda\":2,\n  \"worcestershire sauce\":2,\n  \"brown sugar\":9,\n  \"balsamic vinegar\":2,\n  \"ginger\":18,\n  \"lime juice\":12,\n  \"dry mustard\":3,\n  \"raw shrimp\":2,\n  \"lime\":6,\n  \"mayonnaise\":2,\n  \"peanut butter\":6,\n  \"green onions\":43,\n  \"cumin\":2,\n  \"olive oil\":2,\n  \"vegetable oil\":37,\n  \"flour\":5,\n  \"curry powder\":4,\n  \"shortening\":2,\n  \"vinegar\":6,\n  \"sherry\":22,\n  \"carrots\":18,\n  \"chicken breast\":10,\n  \"sugar\":74,\n  \"mushrooms\":30},\n \"ground cinnamon\":\n {\"elbow macaroni\":2,\n  \"red wine\":8,\n  \"canned tomato sauce\":3,\n  \"bacon\":5,\n  \"cayenne pepper\":46,\n  \"honey\":146,\n  \"lemon juice\":117,\n  \"paprika\":22,\n  \"bread\":7,\n  \"yeast\":9,\n  \"egg yolks\":30,\n  \"green pepper\":7,\n  \"baking powder\":392,\n  \"saffron\":2,\n  \"sesame oil\":3,\n  \"cinnamon\":10,\n  \"pine nuts\":16,\n  \"cream of tartar\":11,\n  \"cheddar cheese\":5,\n  \"corn tortilla\":6,\n  \"tomatoes\":23,\n  \"white pepper\":5,\n  \"brown rice\":7,\n  \"black beans\":4,\n  \"pinto beans\":3,\n  \"cauliflower\":2,\n  \"frozen peas\":2,\n  \"butter\":270,\n  \"orange juice\":72,\n  \"lemon\":25,\n  \"black pepper\":30,\n  \"tumeric\":2,\n  \"black olives\":2,\n  \"buttermilk\":58,\n  \"powdered sugar\":71,\n  \"eggplant\":4,\n  \"dried oregano\":20,\n  \"cornmeal\":9,\n  \"cloves\":23,\n  \"oil\":34,\n  \"chicken\":16,\n  \"canola oil\":17,\n  \"dried tarragon\":2,\n  \"strawberries\":5,\n  \"celery\":7,\n  \"cornish game hens\":2,\n  \"walnuts\":67,\n  \"nutmeg\":37,\n  \"fresh peaches\":4,\n  \"fresh cilantro\":7,\n  \"baking soda\":400,\n  \"bread crumbs\":7,\n  \"tabasco sauce\":5,\n  \"corn syrup\":7,\n  \"balsamic vinegar\":2,\n  \"ginger\":8,\n  \"fresh rosemary\":2,\n  \"lime\":4,\n  \"pecans\":45,\n  \"peanut butter\":5,\n  \"frozen corn\":2,\n  \"green onions\":14,\n  \"cumin\":6,\n  \"skim milk\":69,\n  \"banana\":10,\n  \"beets\":4,\n  \"barley\":2,\n  \"kidney beans\":2,\n  \"shortening\":47,\n  \"applesauce\":33,\n  \"carrots\":41,\n  \"mushrooms\":4,\n  \"sour cream\":46,\n  \"yogurt\":4,\n  \"cream cheese\":24,\n  \"salsa\":2,\n  \"parsley\":4,\n  \"green olives\":3,\n  \"molasses\":77,\n  \"beer\":5,\n  \"chili powder\":37,\n  \"raisins\":223,\n  \"red wine vinegar\":14,\n  \"chives\":2,\n  \"garam masala\":5,\n  \"cornstarch\":65,\n  \"orange\":25,\n  \"dried basil\":3,\n  \"catsup\":4,\n  \"ground beef\":9,\n  \"cumin seeds\":11,\n  \"barbecue sauce\":2,\n  \"red pepper flakes\":3,\n  \"eggs\":428,\n  \"almonds\":26,\n  \"margarine\":133,\n  \"fresh ginger\":10,\n  \"garlic powder\":15,\n  \"dried thyme\":19,\n  \"low fat milk\":3,\n  \"vanilla\":141,\n  \"whole wheat flour\":54,\n  \"salt\":843,\n  \"potatoes\":10,\n  \"almond extract\":17,\n  \"frozen spinach\":2,\n  \"canned apricots\":3,\n  \"celery seeds\":2,\n  \"allspice\":19,\n  \"dijon mustard\":2,\n  \"soy sauce\":22,\n  \"broccoli\":2,\n  \"apples\":58,\n  \"white rice\":4,\n  \"sweet potatoes\":21,\n  \"maple syrup\":34,\n  \"egg whites\":87,\n  \"worcestershire sauce\":8,\n  \"brown sugar\":209,\n  \"lime juice\":16,\n  \"dry mustard\":18,\n  \"white flour\":2,\n  \"blueberries\":14,\n  \"mayonnaise\":5,\n  \"chicken wings\":3,\n  \"fresh thyme\":4,\n  \"vegetable oil\":184,\n  \"olive oil\":72,\n  \"flour\":200,\n  \"curry powder\":20,\n  \"vinegar\":24,\n  \"white wine\":5,\n  \"oats\":7,\n  \"whipping cream\":36,\n  \"sugar\":843},\n \"sesame oil\":\n {\"red wine\":8,\n  \"bacon\":3,\n  \"cayenne pepper\":14,\n  \"lemon juice\":37,\n  \"honey\":58,\n  \"paprika\":6,\n  \"ground cinnamon\":3,\n  \"yeast\":2,\n  \"egg yolks\":4,\n  \"green pepper\":16,\n  \"baking powder\":7,\n  \"pine nuts\":2,\n  \"cinnamon\":2,\n  \"avocado\":2,\n  \"tomatoes\":9,\n  \"white pepper\":54,\n  \"brown rice\":4,\n  \"black beans\":5,\n  \"fresh garlic\":2,\n  \"cauliflower\":3,\n  \"frozen peas\":5,\n  \"butter\":7,\n  \"orange juice\":13,\n  \"lemon\":4,\n  \"black pepper\":46,\n  \"tumeric\":3,\n  \"hoisin sauce\":48,\n  \"eggplant\":5,\n  \"cloves\":3,\n  \"oil\":100,\n  \"chicken\":15,\n  \"blue cheese\":2,\n  \"cucumber\":29,\n  \"canola oil\":10,\n  \"dried tarragon\":2,\n  \"celery\":23,\n  \"alfalfa sprouts\":3,\n  \"walnuts\":5,\n  \"nutmeg\":3,\n  \"fresh cilantro\":13,\n  \"bread crumbs\":3,\n  \"baking soda\":8,\n  \"tabasco sauce\":10,\n  \"ginger\":82,\n  \"balsamic vinegar\":9,\n  \"lime\":3,\n  \"pecans\":2,\n  \"peanut butter\":22,\n  \"cumin\":9,\n  \"green onions\":121,\n  \"shortening\":2,\n  \"carrots\":49,\n  \"mushrooms\":35,\n  \"sour cream\":3,\n  \"yogurt\":2,\n  \"brussels sprouts\":2,\n  \"tofu\":21,\n  \"cherry tomatoes\":3,\n  \"parsley\":8,\n  \"rice vinegar\":66,\n  \"molasses\":2,\n  \"beer\":3,\n  \"chili powder\":14,\n  \"spaghetti\":10,\n  \"red wine vinegar\":17,\n  \"chives\":3,\n  \"cornstarch\":266,\n  \"dried basil\":2,\n  \"orange\":4,\n  \"radishes\":6,\n  \"catsup\":5,\n  \"ground beef\":9,\n  \"romaine lettuce\":2,\n  \"sweet red pepper\":9,\n  \"cumin seeds\":2,\n  \"red pepper flakes\":15,\n  \"almonds\":4,\n  \"eggs\":51,\n  \"fresh ginger\":47,\n  \"kale\":2,\n  \"garlic powder\":11,\n  \"dried thyme\":2,\n  \"whole wheat flour\":2,\n  \"salt\":391,\n  \"fresh basil\":4,\n  \"bean sprouts\":28,\n  \"shiitake mushrooms\":13,\n  \"potatoes\":2,\n  \"dijon mustard\":14,\n  \"soy sauce\":554,\n  \"broccoli\":18,\n  \"chicken legs\":3,\n  \"white rice\":2,\n  \"maple syrup\":3,\n  \"sweet potatoes\":2,\n  \"egg whites\":10,\n  \"worcestershire sauce\":9,\n  \"dried cilantro\":2,\n  \"brown sugar\":42,\n  \"lime juice\":15,\n  \"dry mustard\":9,\n  \"raw shrimp\":6,\n  \"mayonnaise\":6,\n  \"chicken wings\":6,\n  \"vegetable oil\":126,\n  \"olive oil\":37,\n  \"flour\":35,\n  \"curry powder\":11,\n  \"vinegar\":50,\n  \"white wine\":15,\n  \"sherry\":89,\n  \"whipping cream\":3,\n  \"chicken breast\":21,\n  \"sugar\":391},\n \"cream of tartar\":\n {\"sour cream\":17,\n  \"yogurt\":2,\n  \"tofu\":3,\n  \"cream cheese\":6,\n  \"bacon\":2,\n  \"cayenne pepper\":4,\n  \"lemon juice\":46,\n  \"honey\":16,\n  \"swiss cheese\":3,\n  \"molasses\":8,\n  \"beer\":2,\n  \"paprika\":2,\n  \"ground cinnamon\":11,\n  \"raisins\":7,\n  \"yeast\":4,\n  \"chives\":2,\n  \"egg yolks\":74,\n  \"green pepper\":2,\n  \"cornstarch\":58,\n  \"baking powder\":78,\n  \"orange\":4,\n  \"cinnamon\":40,\n  \"tomatoes\":2,\n  \"white pepper\":3,\n  \"eggs\":141,\n  \"almonds\":7,\n  \"margarine\":26,\n  \"fresh ginger\":2,\n  \"butter\":120,\n  \"garlic powder\":2,\n  \"dried thyme\":2,\n  \"orange juice\":15,\n  \"vanilla\":208,\n  \"whole wheat flour\":8,\n  \"black pepper\":3,\n  \"lemon\":6,\n  \"salt\":344,\n  \"tumeric\":3,\n  \"potatoes\":2,\n  \"buttermilk\":14,\n  \"powdered sugar\":49,\n  \"almond extract\":39,\n  \"dried oregano\":2,\n  \"cornmeal\":6,\n  \"cloves\":2,\n  \"oil\":17,\n  \"celery seeds\":2,\n  \"allspice\":2,\n  \"canola oil\":6,\n  \"strawberries\":9,\n  \"broccoli\":2,\n  \"apples\":2,\n  \"maple syrup\":3,\n  \"sweet potatoes\":3,\n  \"nutmeg\":20,\n  \"walnuts\":13,\n  \"egg whites\":254,\n  \"bread crumbs\":4,\n  \"baking soda\":142,\n  \"corn syrup\":8,\n  \"brown sugar\":34,\n  \"ginger\":9,\n  \"lime juice\":7,\n  \"dry mustard\":2,\n  \"lime\":2,\n  \"white flour\":3,\n  \"pecans\":9,\n  \"blueberries\":2,\n  \"peanut butter\":4,\n  \"vegetable oil\":20,\n  \"banana\":5,\n  \"skim milk\":11,\n  \"flour\":154,\n  \"shortening\":46,\n  \"vinegar\":8,\n  \"applesauce\":4,\n  \"parmesan cheese\":3,\n  \"white wine\":2,\n  \"sherry\":3,\n  \"oats\":3,\n  \"whipping cream\":37,\n  \"mushrooms\":2,\n  \"sugar\":479},\n \"garlic powder\":\n {\"canned beef broth\":2,\n  \"elbow macaroni\":4,\n  \"red wine\":7,\n  \"canned tomato sauce\":7,\n  \"bacon\":35,\n  \"canned clams\":2,\n  \"cayenne pepper\":167,\n  \"lemon juice\":171,\n  \"honey\":48,\n  \"zucchini squash\":2,\n  \"swiss cheese\":9,\n  \"paprika\":365,\n  \"bread\":5,\n  \"ground cinnamon\":15,\n  \"yeast\":19,\n  \"egg yolks\":5,\n  \"green pepper\":91,\n  \"baking powder\":22,\n  \"saffron\":4,\n  \"sesame oil\":11,\n  \"cream of tartar\":2,\n  \"pine nuts\":3,\n  \"cinnamon\":26,\n  \"avocado\":5,\n  \"cheddar cheese\":43,\n  \"tomatoes\":77,\n  \"white pepper\":120,\n  \"brown rice\":20,\n  \"black beans\":9,\n  \"fresh garlic\":3,\n  \"pinto beans\":13,\n  \"cauliflower\":7,\n  \"butter\":144,\n  \"frozen peas\":12,\n  \"salmon\":4,\n  \"orange juice\":19,\n  \"lemon\":21,\n  \"black pepper\":273,\n  \"tumeric\":6,\n  \"black olives\":11,\n  \"buttermilk\":15,\n  \"hoisin sauce\":2,\n  \"dried oregano\":108,\n  \"eggplant\":14,\n  \"capers\":6,\n  \"cornmeal\":22,\n  \"cloves\":8,\n  \"oil\":124,\n  \"chicken\":42,\n  \"blue cheese\":4,\n  \"cucumber\":10,\n  \"flour tortilla\":2,\n  \"canola oil\":6,\n  \"dried tarragon\":10,\n  \"celery\":62,\n  \"alfalfa sprouts\":3,\n  \"walnuts\":9,\n  \"nutmeg\":26,\n  \"fresh cilantro\":9,\n  \"baking soda\":6,\n  \"bread crumbs\":41,\n  \"corn syrup\":2,\n  \"tabasco sauce\":53,\n  \"ginger\":63,\n  \"balsamic vinegar\":5,\n  \"lime\":4,\n  \"pecans\":4,\n  \"peanut butter\":8,\n  \"frozen corn\":3,\n  \"green onions\":62,\n  \"cumin\":134,\n  \"canned chicken broth\":2,\n  \"skim milk\":22,\n  \"beets\":3,\n  \"barley\":8,\n  \"kidney beans\":18,\n  \"shortening\":8,\n  \"parmesan cheese\":107,\n  \"applesauce\":3,\n  \"carrots\":58,\n  \"mushrooms\":55,\n  \"sour cream\":74,\n  \"yogurt\":5,\n  \"cream cheese\":55,\n  \"tofu\":16,\n  \"cherry tomatoes\":10,\n  \"salsa\":14,\n  \"parsley\":78,\n  \"green olives\":5,\n  \"rice vinegar\":3,\n  \"molasses\":22,\n  \"chili powder\":262,\n  \"beer\":16,\n  \"spaghetti\":9,\n  \"raisins\":7,\n  \"red wine vinegar\":43,\n  \"garam masala\":3,\n  \"chives\":15,\n  \"cornstarch\":73,\n  \"orange\":6,\n  \"dried basil\":94,\n  \"catsup\":40,\n  \"ground beef\":93,\n  \"adzuki beans\":2,\n  \"romaine lettuce\":4,\n  \"mozzarella cheese\":36,\n  \"sweet red pepper\":4,\n  \"cumin seeds\":5,\n  \"canned peaches\":2,\n  \"barbecue sauce\":11,\n  \"red pepper flakes\":13,\n  \"almonds\":7,\n  \"eggs\":101,\n  \"margarine\":74,\n  \"fresh ginger\":6,\n  \"dried thyme\":69,\n  \"cream of mushroom soup\":18,\n  \"low fat milk\":2,\n  \"whole wheat flour\":28,\n  \"salt\":935,\n  \"fresh basil\":8,\n  \"bean sprouts\":2,\n  \"potatoes\":45,\n  \"frozen spinach\":6,\n  \"celery seeds\":6,\n  \"allspice\":22,\n  \"dijon mustard\":33,\n  \"soy sauce\":214,\n  \"broccoli\":12,\n  \"chicken legs\":2,\n  \"apples\":3,\n  \"maple syrup\":3,\n  \"yellow mustard\":3,\n  \"shell macaroni\":4,\n  \"white rice\":4,\n  \"sweet potatoes\":3,\n  \"canned tomato paste\":2,\n  \"egg whites\":20,\n  \"worcestershire sauce\":192,\n  \"dried cilantro\":3,\n  \"brown sugar\":113,\n  \"lime juice\":18,\n  \"dry mustard\":133,\n  \"raw shrimp\":5,\n  \"white flour\":6,\n  \"mayonnaise\":86,\n  \"chicken wings\":18,\n  \"fresh thyme\":2,\n  \"vegetable oil\":129,\n  \"olive oil\":159,\n  \"flour\":164,\n  \"curry powder\":42,\n  \"vinegar\":76,\n  \"white wine\":21,\n  \"sherry\":20,\n  \"oats\":5,\n  \"whipping cream\":4,\n  \"chicken breast\":9,\n  \"sugar\":192},\n \"cornmeal\":\n {\"bacon\":8,\n  \"cayenne pepper\":15,\n  \"lemon juice\":7,\n  \"honey\":44,\n  \"swiss cheese\":2,\n  \"paprika\":19,\n  \"bread\":2,\n  \"ground cinnamon\":9,\n  \"yeast\":52,\n  \"egg yolks\":11,\n  \"green pepper\":4,\n  \"baking powder\":161,\n  \"saffron\":2,\n  \"pine nuts\":5,\n  \"cinnamon\":22,\n  \"cream of tartar\":6,\n  \"avocado\":2,\n  \"cheddar cheese\":25,\n  \"tomatoes\":5,\n  \"white pepper\":5,\n  \"brown rice\":2,\n  \"pinto beans\":2,\n  \"butter\":74,\n  \"orange juice\":3,\n  \"lemon\":7,\n  \"black pepper\":28,\n  \"black olives\":3,\n  \"buttermilk\":64,\n  \"eggplant\":3,\n  \"dried oregano\":14,\n  \"cloves\":2,\n  \"oil\":42,\n  \"chicken\":3,\n  \"canola oil\":6,\n  \"strawberries\":2,\n  \"celery\":5,\n  \"fresh cilantro\":3,\n  \"walnuts\":6,\n  \"nutmeg\":13,\n  \"bread crumbs\":6,\n  \"baking soda\":93,\n  \"tabasco sauce\":2,\n  \"ginger\":11,\n  \"balsamic vinegar\":4,\n  \"fresh rosemary\":3,\n  \"pecans\":3,\n  \"peanut butter\":3,\n  \"frozen corn\":3,\n  \"cumin\":14,\n  \"green onions\":3,\n  \"banana\":2,\n  \"skim milk\":11,\n  \"kidney beans\":3,\n  \"shortening\":32,\n  \"applesauce\":14,\n  \"parmesan cheese\":13,\n  \"carrots\":4,\n  \"mushrooms\":2,\n  \"sour cream\":16,\n  \"yogurt\":3,\n  \"cream cheese\":4,\n  \"tofu\":2,\n  \"salsa\":2,\n  \"parsley\":10,\n  \"molasses\":40,\n  \"chili powder\":35,\n  \"beer\":4,\n  \"raisins\":14,\n  \"red wine vinegar\":2,\n  \"chives\":2,\n  \"cornstarch\":8,\n  \"dried basil\":9,\n  \"catsup\":2,\n  \"ground beef\":13,\n  \"mozzarella cheese\":5,\n  \"sweet red pepper\":3,\n  \"barbecue sauce\":3,\n  \"red pepper flakes\":3,\n  \"almonds\":2,\n  \"eggs\":107,\n  \"margarine\":21,\n  \"garlic powder\":22,\n  \"kale\":2,\n  \"dried thyme\":11,\n  \"vanilla\":11,\n  \"whole wheat flour\":57,\n  \"salt\":438,\n  \"fresh basil\":3,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":2,\n  \"almond extract\":2,\n  \"allspice\":4,\n  \"dijon mustard\":2,\n  \"soy sauce\":4,\n  \"apples\":2,\n  \"maple syrup\":8,\n  \"egg whites\":28,\n  \"worcestershire sauce\":6,\n  \"brown sugar\":21,\n  \"dry mustard\":10,\n  \"white flour\":16,\n  \"blueberries\":4,\n  \"mayonnaise\":3,\n  \"fresh thyme\":3,\n  \"olive oil\":66,\n  \"vegetable oil\":62,\n  \"flour\":160,\n  \"vinegar\":5,\n  \"oats\":5,\n  \"whipping cream\":4,\n  \"sugar\":195},\n \"fresh thyme\":\n {\"sour cream\":6,\n  \"red wine\":3,\n  \"cream cheese\":2,\n  \"cherry tomatoes\":2,\n  \"bacon\":6,\n  \"cayenne pepper\":6,\n  \"parsley\":14,\n  \"green olives\":2,\n  \"honey\":12,\n  \"lemon juice\":13,\n  \"paprika\":2,\n  \"chili powder\":2,\n  \"spaghetti\":2,\n  \"ground cinnamon\":4,\n  \"raisins\":3,\n  \"red wine vinegar\":10,\n  \"chives\":2,\n  \"green pepper\":2,\n  \"cornstarch\":3,\n  \"baking powder\":2,\n  \"orange\":5,\n  \"saffron\":2,\n  \"radishes\":2,\n  \"pine nuts\":2,\n  \"cinnamon\":3,\n  \"mozzarella cheese\":2,\n  \"tomatoes\":11,\n  \"white pepper\":4,\n  \"brown rice\":2,\n  \"red pepper flakes\":3,\n  \"eggs\":9,\n  \"black beans\":2,\n  \"margarine\":3,\n  \"butter\":22,\n  \"kale\":2,\n  \"garlic powder\":2,\n  \"dried thyme\":16,\n  \"orange juice\":3,\n  \"whole wheat flour\":2,\n  \"lemon\":6,\n  \"salt\":81,\n  \"black pepper\":13,\n  \"fresh basil\":20,\n  \"black olives\":2,\n  \"shiitake mushrooms\":3,\n  \"buttermilk\":2,\n  \"potatoes\":8,\n  \"dried oregano\":3,\n  \"capers\":3,\n  \"cornmeal\":3,\n  \"cloves\":2,\n  \"oil\":5,\n  \"chicken\":4,\n  \"cucumber\":2,\n  \"canola oil\":5,\n  \"allspice\":3,\n  \"dried tarragon\":2,\n  \"dijon mustard\":10,\n  \"soy sauce\":5,\n  \"celery\":8,\n  \"sweet potatoes\":3,\n  \"fresh cilantro\":3,\n  \"walnuts\":2,\n  \"nutmeg\":6,\n  \"worcestershire sauce\":4,\n  \"brown sugar\":4,\n  \"ginger\":2,\n  \"balsamic vinegar\":10,\n  \"lime juice\":3,\n  \"dry mustard\":3,\n  \"fresh rosemary\":14,\n  \"raw shrimp\":2,\n  \"pecans\":4,\n  \"mayonnaise\":2,\n  \"green onions\":8,\n  \"cumin\":2,\n  \"vegetable oil\":15,\n  \"olive oil\":72,\n  \"skim milk\":2,\n  \"barley\":3,\n  \"flour\":6,\n  \"curry powder\":3,\n  \"vinegar\":4,\n  \"parmesan cheese\":9,\n  \"white wine\":9,\n  \"sherry\":2,\n  \"carrots\":14,\n  \"whipping cream\":4,\n  \"mushrooms\":10,\n  \"sugar\":10},\n \"red leaf lettuce\":\n {\"olive oil\":2,\n  \"vegetable oil\":2,\n  \"cornstarch\":2,\n  \"flour\":2,\n  \"avocado\":2,\n  \"dried oregano\":2,\n  \"mayonnaise\":2,\n  \"salt\":2,\n  \"sugar\":2,\n  \"black pepper\":2},\n \"raw spinach\":\n {\"sour cream\":2,\n  \"bean sprouts\":2,\n  \"potatoes\":2,\n  \"bacon\":2,\n  \"tomatoes\":2,\n  \"parsley\":2,\n  \"green onions\":2,\n  \"olive oil\":2,\n  \"vegetable oil\":2,\n  \"eggs\":4,\n  \"flour\":3,\n  \"paprika\":2,\n  \"butter\":3,\n  \"vinegar\":2,\n  \"parmesan cheese\":2,\n  \"carrots\":2,\n  \"green pepper\":2,\n  \"salt\":3},\n \"brown sugar\":\n {\"canned beef broth\":2,\n  \"elbow macaroni\":2,\n  \"red wine\":25,\n  \"canned tomato sauce\":4,\n  \"bacon\":83,\n  \"brown onions\":2,\n  \"cayenne pepper\":72,\n  \"lemon juice\":332,\n  \"honey\":160,\n  \"paprika\":115,\n  \"bread\":10,\n  \"ground cinnamon\":209,\n  \"yeast\":100,\n  \"egg yolks\":61,\n  \"green pepper\":72,\n  \"baking powder\":740,\n  \"saffron\":3,\n  \"sesame oil\":42,\n  \"pine nuts\":2,\n  \"cinnamon\":891,\n  \"cream of tartar\":34,\n  \"cheddar cheese\":10,\n  \"corn tortilla\":2,\n  \"tomatoes\":55,\n  \"white pepper\":25,\n  \"black beans\":4,\n  \"fresh garlic\":3,\n  \"pinto beans\":6,\n  \"cauliflower\":3,\n  \"butter\":1081,\n  \"salmon\":2,\n  \"orange juice\":135,\n  \"black pepper\":145,\n  \"lemon\":55,\n  \"tumeric\":7,\n  \"black olives\":5,\n  \"buttermilk\":97,\n  \"powdered sugar\":69,\n  \"hoisin sauce\":4,\n  \"eggplant\":3,\n  \"dried oregano\":15,\n  \"capers\":6,\n  \"cornmeal\":21,\n  \"cloves\":190,\n  \"oil\":202,\n  \"chicken\":21,\n  \"cucumber\":10,\n  \"canola oil\":17,\n  \"dried tarragon\":7,\n  \"strawberries\":11,\n  \"celery\":40,\n  \"alfalfa sprouts\":2,\n  \"cornish game hens\":7,\n  \"fresh cilantro\":4,\n  \"fresh peaches\":8,\n  \"nutmeg\":371,\n  \"walnuts\":149,\n  \"baking soda\":903,\n  \"bread crumbs\":19,\n  \"corn syrup\":60,\n  \"tabasco sauce\":44,\n  \"balsamic vinegar\":27,\n  \"ginger\":187,\n  \"lime\":12,\n  \"pecans\":151,\n  \"peanut butter\":123,\n  \"frozen corn\":2,\n  \"green onions\":46,\n  \"cumin\":40,\n  \"beets\":9,\n  \"skim milk\":56,\n  \"banana\":17,\n  \"barley\":2,\n  \"kidney beans\":20,\n  \"shortening\":286,\n  \"parmesan cheese\":10,\n  \"applesauce\":91,\n  \"carrots\":69,\n  \"mushrooms\":16,\n  \"sour cream\":84,\n  \"yogurt\":5,\n  \"tofu\":4,\n  \"cream cheese\":76,\n  \"cherry tomatoes\":5,\n  \"salsa\":5,\n  \"parsley\":30,\n  \"rice vinegar\":15,\n  \"molasses\":194,\n  \"chili powder\":142,\n  \"beer\":43,\n  \"spaghetti\":2,\n  \"raisins\":374,\n  \"red wine vinegar\":76,\n  \"chives\":2,\n  \"garam masala\":6,\n  \"cornstarch\":216,\n  \"dried basil\":9,\n  \"orange\":32,\n  \"radishes\":2,\n  \"catsup\":159,\n  \"ground beef\":75,\n  \"romaine lettuce\":3,\n  \"sweet red pepper\":4,\n  \"cumin seeds\":9,\n  \"canned peaches\":2,\n  \"barbecue sauce\":17,\n  \"red pepper flakes\":21,\n  \"eggs\":845,\n  \"almonds\":44,\n  \"margarine\":309,\n  \"fresh ginger\":25,\n  \"garlic powder\":113,\n  \"dried thyme\":22,\n  \"cream of mushroom soup\":3,\n  \"low fat milk\":2,\n  \"vanilla\":926,\n  \"whole wheat flour\":136,\n  \"salt\":2255,\n  \"fresh basil\":6,\n  \"bean sprouts\":9,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":18,\n  \"almond extract\":50,\n  \"celery seeds\":8,\n  \"allspice\":137,\n  \"dijon mustard\":44,\n  \"soy sauce\":335,\n  \"broccoli\":3,\n  \"chicken legs\":2,\n  \"apples\":155,\n  \"maple syrup\":37,\n  \"sweet potatoes\":47,\n  \"yellow mustard\":9,\n  \"egg whites\":109,\n  \"worcestershire sauce\":272,\n  \"dried cilantro\":2,\n  \"lime juice\":46,\n  \"dry mustard\":231,\n  \"white flour\":35,\n  \"blueberries\":13,\n  \"mayonnaise\":10,\n  \"chicken wings\":26,\n  \"fresh thyme\":4,\n  \"olive oil\":88,\n  \"vegetable oil\":204,\n  \"flour\":1252,\n  \"curry powder\":33,\n  \"vinegar\":337,\n  \"white wine\":19,\n  \"sherry\":42,\n  \"oats\":81,\n  \"whipping cream\":34,\n  \"chicken breast\":8,\n  \"sugar\":782},\n \"whipping cream\":\n {\"red wine\":3,\n  \"elbow macaroni\":4,\n  \"bacon\":16,\n  \"cayenne pepper\":17,\n  \"lemon juice\":138,\n  \"honey\":19,\n  \"swiss cheese\":7,\n  \"paprika\":17,\n  \"camembert cheese\":2,\n  \"ground cinnamon\":36,\n  \"bread\":4,\n  \"dried bay leaf\":2,\n  \"yeast\":3,\n  \"egg yolks\":186,\n  \"green pepper\":4,\n  \"baking powder\":68,\n  \"saffron\":5,\n  \"sesame oil\":3,\n  \"pine nuts\":3,\n  \"cinnamon\":47,\n  \"cream of tartar\":37,\n  \"cheddar cheese\":8,\n  \"tomatoes\":13,\n  \"white pepper\":28,\n  \"black beans\":2,\n  \"cauliflower\":3,\n  \"butter\":364,\n  \"frozen peas\":6,\n  \"salmon\":4,\n  \"orange juice\":28,\n  \"lemon\":29,\n  \"black pepper\":9,\n  \"tumeric\":2,\n  \"black olives\":3,\n  \"buttermilk\":17,\n  \"powdered sugar\":89,\n  \"dried oregano\":5,\n  \"eggplant\":2,\n  \"capers\":5,\n  \"cornmeal\":4,\n  \"cloves\":10,\n  \"oil\":25,\n  \"chicken\":8,\n  \"cucumber\":2,\n  \"dried tarragon\":8,\n  \"strawberries\":43,\n  \"celery\":14,\n  \"fresh cilantro\":2,\n  \"nutmeg\":51,\n  \"fresh peaches\":4,\n  \"walnuts\":16,\n  \"bread crumbs\":6,\n  \"baking soda\":23,\n  \"tabasco sauce\":4,\n  \"corn syrup\":7,\n  \"ginger\":10,\n  \"fresh rosemary\":2,\n  \"lime\":3,\n  \"pecans\":33,\n  \"peanut butter\":7,\n  \"cumin\":2,\n  \"green onions\":20,\n  \"frozen corn\":4,\n  \"skim milk\":2,\n  \"banana\":7,\n  \"shortening\":9,\n  \"applesauce\":4,\n  \"parmesan cheese\":30,\n  \"carrots\":12,\n  \"mushrooms\":24,\n  \"sour cream\":45,\n  \"yogurt\":2,\n  \"cream cheese\":95,\n  \"cherry tomatoes\":2,\n  \"parsley\":18,\n  \"molasses\":3,\n  \"chili powder\":4,\n  \"spaghetti\":4,\n  \"raisins\":21,\n  \"red wine vinegar\":2,\n  \"chives\":5,\n  \"garam masala\":3,\n  \"cornstarch\":77,\n  \"orange\":14,\n  \"dried basil\":7,\n  \"radishes\":2,\n  \"catsup\":3,\n  \"ground beef\":3,\n  \"mozzarella cheese\":4,\n  \"cumin seeds\":4,\n  \"red pepper flakes\":2,\n  \"eggs\":312,\n  \"almonds\":25,\n  \"margarine\":30,\n  \"fresh ginger\":4,\n  \"garlic powder\":4,\n  \"dried thyme\":12,\n  \"cream of mushroom soup\":3,\n  \"vanilla\":265,\n  \"whole wheat flour\":2,\n  \"salt\":450,\n  \"fresh basil\":11,\n  \"potatoes\":14,\n  \"almond extract\":31,\n  \"frozen spinach\":2,\n  \"celery seeds\":2,\n  \"allspice\":5,\n  \"dijon mustard\":33,\n  \"soy sauce\":3,\n  \"chicken legs\":2,\n  \"apples\":10,\n  \"maple syrup\":12,\n  \"sweet potatoes\":4,\n  \"egg whites\":92,\n  \"worcestershire sauce\":26,\n  \"brown sugar\":34,\n  \"lime juice\":10,\n  \"dry mustard\":7,\n  \"raw shrimp\":2,\n  \"white flour\":2,\n  \"blueberries\":3,\n  \"mayonnaise\":25,\n  \"fresh thyme\":4,\n  \"vegetable oil\":31,\n  \"olive oil\":67,\n  \"flour\":145,\n  \"curry powder\":11,\n  \"vinegar\":6,\n  \"white wine\":16,\n  \"sherry\":13,\n  \"sugar\":750},\n \"salsa\":\n {\"red wine\":2,\n  \"bacon\":4,\n  \"cayenne pepper\":14,\n  \"lemon juice\":9,\n  \"honey\":3,\n  \"swiss cheese\":2,\n  \"paprika\":6,\n  \"ground cinnamon\":2,\n  \"yeast\":2,\n  \"green pepper\":19,\n  \"baking powder\":6,\n  \"cinnamon\":3,\n  \"avocado\":8,\n  \"cheddar cheese\":24,\n  \"tomatoes\":15,\n  \"white pepper\":5,\n  \"brown rice\":4,\n  \"black beans\":12,\n  \"pinto beans\":10,\n  \"fresh garlic\":2,\n  \"butter\":12,\n  \"orange juice\":3,\n  \"black pepper\":9,\n  \"black olives\":6,\n  \"buttermilk\":2,\n  \"dried oregano\":13,\n  \"eggplant\":4,\n  \"cornmeal\":2,\n  \"cloves\":2,\n  \"oil\":16,\n  \"chicken\":6,\n  \"cucumber\":2,\n  \"canola oil\":7,\n  \"flour tortilla\":5,\n  \"strawberries\":2,\n  \"celery\":2,\n  \"alfalfa sprouts\":3,\n  \"fresh cilantro\":14,\n  \"baking soda\":2,\n  \"tabasco sauce\":3,\n  \"balsamic vinegar\":2,\n  \"lime\":4,\n  \"pecans\":4,\n  \"cumin\":22,\n  \"green onions\":23,\n  \"frozen corn\":4,\n  \"skim milk\":5,\n  \"kidney beans\":5,\n  \"shortening\":2,\n  \"parmesan cheese\":3,\n  \"carrots\":3,\n  \"mushrooms\":8,\n  \"sour cream\":61,\n  \"yogurt\":3,\n  \"tofu\":4,\n  \"cream cheese\":10,\n  \"green olives\":2,\n  \"chili powder\":50,\n  \"beer\":2,\n  \"raisins\":3,\n  \"red wine vinegar\":3,\n  \"chives\":3,\n  \"cornstarch\":8,\n  \"orange\":2,\n  \"dried basil\":4,\n  \"catsup\":4,\n  \"radishes\":2,\n  \"ground beef\":16,\n  \"mozzarella cheese\":7,\n  \"sweet red pepper\":3,\n  \"cumin seeds\":2,\n  \"barbecue sauce\":2,\n  \"red pepper flakes\":2,\n  \"eggs\":30,\n  \"margarine\":3,\n  \"garlic powder\":14,\n  \"dried thyme\":2,\n  \"salt\":65,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":6,\n  \"frozen spinach\":2,\n  \"dijon mustard\":7,\n  \"soy sauce\":5,\n  \"apples\":2,\n  \"white rice\":2,\n  \"egg whites\":5,\n  \"worcestershire sauce\":8,\n  \"brown sugar\":5,\n  \"lime juice\":19,\n  \"dry mustard\":2,\n  \"white flour\":2,\n  \"mayonnaise\":6,\n  \"olive oil\":30,\n  \"vegetable oil\":26,\n  \"flour\":5,\n  \"vinegar\":3,\n  \"white wine\":2,\n  \"sherry\":2,\n  \"chicken breast\":5,\n  \"sugar\":7},\n \"paprika\":\n {\"canned beef broth\":3,\n  \"elbow macaroni\":9,\n  \"red wine\":21,\n  \"canned tomato sauce\":4,\n  \"bacon\":50,\n  \"brown onions\":5,\n  \"cayenne pepper\":270,\n  \"lemon juice\":283,\n  \"honey\":52,\n  \"zucchini squash\":2,\n  \"swiss cheese\":14,\n  \"camembert cheese\":3,\n  \"ground cinnamon\":22,\n  \"bread\":23,\n  \"yeast\":7,\n  \"egg yolks\":27,\n  \"green pepper\":95,\n  \"baking powder\":19,\n  \"saffron\":18,\n  \"sesame oil\":6,\n  \"pine nuts\":2,\n  \"cream of tartar\":2,\n  \"cinnamon\":64,\n  \"avocado\":6,\n  \"cheddar cheese\":53,\n  \"tomatoes\":143,\n  \"white pepper\":118,\n  \"brown rice\":14,\n  \"black beans\":7,\n  \"fresh garlic\":2,\n  \"pinto beans\":10,\n  \"cauliflower\":6,\n  \"frozen peas\":13,\n  \"butter\":414,\n  \"salmon\":5,\n  \"orange juice\":28,\n  \"lemon\":63,\n  \"black pepper\":328,\n  \"tumeric\":12,\n  \"black olives\":8,\n  \"buttermilk\":14,\n  \"powdered sugar\":2,\n  \"eggplant\":17,\n  \"dried oregano\":66,\n  \"capers\":20,\n  \"cornmeal\":19,\n  \"cloves\":49,\n  \"oil\":176,\n  \"chicken\":76,\n  \"blue cheese\":10,\n  \"cucumber\":7,\n  \"flour tortilla\":2,\n  \"canola oil\":18,\n  \"dried tarragon\":12,\n  \"strawberries\":2,\n  \"celery\":82,\n  \"cornish game hens\":4,\n  \"walnuts\":6,\n  \"nutmeg\":73,\n  \"fresh cilantro\":9,\n  \"baking soda\":6,\n  \"bread crumbs\":57,\n  \"tabasco sauce\":61,\n  \"ginger\":57,\n  \"balsamic vinegar\":4,\n  \"fresh rosemary\":3,\n  \"lime\":3,\n  \"pecans\":15,\n  \"peanut butter\":4,\n  \"frozen corn\":4,\n  \"cumin\":177,\n  \"green onions\":54,\n  \"canned chicken broth\":3,\n  \"beets\":2,\n  \"banana\":3,\n  \"skim milk\":27,\n  \"barley\":4,\n  \"kidney beans\":14,\n  \"shortening\":30,\n  \"applesauce\":2,\n  \"parmesan cheese\":79,\n  \"carrots\":81,\n  \"mushrooms\":87,\n  \"sour cream\":179,\n  \"yogurt\":18,\n  \"tofu\":6,\n  \"cream cheese\":33,\n  \"cherry tomatoes\":5,\n  \"salsa\":6,\n  \"parsley\":166,\n  \"green olives\":8,\n  \"rice vinegar\":4,\n  \"molasses\":22,\n  \"beer\":43,\n  \"chili powder\":281,\n  \"spaghetti\":4,\n  \"raisins\":14,\n  \"red wine vinegar\":49,\n  \"garam masala\":20,\n  \"chives\":26,\n  \"cornstarch\":67,\n  \"orange\":8,\n  \"dried basil\":31,\n  \"radishes\":3,\n  \"catsup\":60,\n  \"ground beef\":70,\n  \"mozzarella cheese\":11,\n  \"sweet red pepper\":6,\n  \"cumin seeds\":26,\n  \"barbecue sauce\":10,\n  \"raw spinach\":2,\n  \"red pepper flakes\":18,\n  \"eggs\":151,\n  \"almonds\":19,\n  \"margarine\":83,\n  \"fresh ginger\":11,\n  \"kale\":2,\n  \"garlic powder\":365,\n  \"dried thyme\":92,\n  \"cream of mushroom soup\":28,\n  \"whole wheat flour\":4,\n  \"salt\":1611,\n  \"fresh basil\":3,\n  \"bean sprouts\":11,\n  \"potatoes\":126,\n  \"frozen spinach\":3,\n  \"allspice\":46,\n  \"celery seeds\":14,\n  \"dijon mustard\":40,\n  \"soy sauce\":62,\n  \"broccoli\":8,\n  \"chicken legs\":3,\n  \"apples\":8,\n  \"white rice\":4,\n  \"maple syrup\":4,\n  \"yellow mustard\":2,\n  \"sweet potatoes\":3,\n  \"egg whites\":15,\n  \"worcestershire sauce\":248,\n  \"dried cilantro\":3,\n  \"brown sugar\":115,\n  \"lime juice\":27,\n  \"dry mustard\":208,\n  \"raw shrimp\":5,\n  \"white flour\":2,\n  \"mayonnaise\":97,\n  \"chicken wings\":12,\n  \"fresh thyme\":2,\n  \"vegetable oil\":193,\n  \"olive oil\":257,\n  \"flour\":428,\n  \"curry powder\":77,\n  \"vinegar\":145,\n  \"white wine\":54,\n  \"sherry\":50,\n  \"whipping cream\":17,\n  \"chicken breast\":18,\n  \"sugar\":255},\n \"barley\":\n {\"red wine\":2,\n  \"brussels sprouts\":2,\n  \"bacon\":4,\n  \"cayenne pepper\":2,\n  \"parsley\":18,\n  \"lemon juice\":5,\n  \"honey\":3,\n  \"molasses\":2,\n  \"paprika\":4,\n  \"ground cinnamon\":2,\n  \"raisins\":5,\n  \"green pepper\":5,\n  \"cornstarch\":2,\n  \"baking powder\":2,\n  \"orange\":2,\n  \"dried basil\":7,\n  \"catsup\":3,\n  \"cinnamon\":2,\n  \"ground beef\":3,\n  \"cheddar cheese\":3,\n  \"tomatoes\":16,\n  \"barbecue sauce\":2,\n  \"brown rice\":9,\n  \"black beans\":2,\n  \"almonds\":2,\n  \"fresh garlic\":2,\n  \"pinto beans\":2,\n  \"butter\":8,\n  \"frozen peas\":3,\n  \"garlic powder\":8,\n  \"dried thyme\":4,\n  \"orange juice\":3,\n  \"black pepper\":3,\n  \"salt\":42,\n  \"lemon\":2,\n  \"buttermilk\":3,\n  \"potatoes\":12,\n  \"dried oregano\":4,\n  \"oil\":9,\n  \"chicken\":5,\n  \"canola oil\":3,\n  \"dijon mustard\":2,\n  \"soy sauce\":9,\n  \"celery\":23,\n  \"chicken legs\":2,\n  \"maple syrup\":2,\n  \"nutmeg\":3,\n  \"egg whites\":2,\n  \"worcestershire sauce\":5,\n  \"tabasco sauce\":2,\n  \"brown sugar\":2,\n  \"balsamic vinegar\":3,\n  \"ginger\":2,\n  \"fresh rosemary\":2,\n  \"lime\":2,\n  \"cumin\":5,\n  \"green onions\":4,\n  \"frozen corn\":2,\n  \"fresh thyme\":3,\n  \"vegetable oil\":4,\n  \"olive oil\":10,\n  \"skim milk\":4,\n  \"flour\":6,\n  \"curry powder\":4,\n  \"kidney beans\":5,\n  \"shortening\":2,\n  \"vinegar\":3,\n  \"white wine\":2,\n  \"sherry\":2,\n  \"carrots\":36,\n  \"oats\":2,\n  \"mushrooms\":10,\n  \"sugar\":5},\n \"romaine lettuce\":\n {\"sour cream\":2,\n  \"tofu\":2,\n  \"bacon\":4,\n  \"cayenne pepper\":2,\n  \"parsley\":2,\n  \"lemon juice\":9,\n  \"chili powder\":2,\n  \"raisins\":3,\n  \"red wine vinegar\":5,\n  \"green pepper\":2,\n  \"cornstarch\":3,\n  \"dried basil\":2,\n  \"sesame oil\":2,\n  \"radishes\":2,\n  \"avocado\":2,\n  \"cheddar cheese\":2,\n  \"tomatoes\":3,\n  \"cumin seeds\":3,\n  \"white pepper\":2,\n  \"fresh garlic\":2,\n  \"garlic powder\":4,\n  \"salt\":11,\n  \"black pepper\":4,\n  \"lemon\":2,\n  \"bean sprouts\":2,\n  \"black olives\":3,\n  \"eggplant\":2,\n  \"oil\":2,\n  \"blue cheese\":4,\n  \"cucumber\":5,\n  \"canola oil\":2,\n  \"dijon mustard\":3,\n  \"soy sauce\":5,\n  \"celery\":2,\n  \"broccoli\":2,\n  \"alfalfa sprouts\":3,\n  \"walnuts\":2,\n  \"nutmeg\":2,\n  \"bread crumbs\":2,\n  \"worcestershire sauce\":3,\n  \"brown sugar\":3,\n  \"balsamic vinegar\":2,\n  \"lime juice\":2,\n  \"mayonnaise\":3,\n  \"cumin\":2,\n  \"green onions\":4,\n  \"vegetable oil\":6,\n  \"olive oil\":14,\n  \"parmesan cheese\":8,\n  \"chicken breast\":2,\n  \"mushrooms\":3,\n  \"sugar\":6},\n \"fresh peaches\":\n {\"sour cream\":4,\n  \"cream cheese\":2,\n  \"buttermilk\":2,\n  \"powdered sugar\":2,\n  \"almond extract\":3,\n  \"cayenne pepper\":2,\n  \"cloves\":2,\n  \"lemon juice\":8,\n  \"honey\":3,\n  \"rice vinegar\":3,\n  \"cucumber\":2,\n  \"molasses\":4,\n  \"dijon mustard\":2,\n  \"ground cinnamon\":4,\n  \"yeast\":2,\n  \"garam masala\":2,\n  \"egg yolks\":2,\n  \"maple syrup\":4,\n  \"green pepper\":2,\n  \"nutmeg\":13,\n  \"baking soda\":4,\n  \"bread crumbs\":2,\n  \"baking powder\":12,\n  \"cornstarch\":7,\n  \"brown sugar\":8,\n  \"ginger\":2,\n  \"cinnamon\":13,\n  \"lime juice\":2,\n  \"cumin\":3,\n  \"green onions\":2,\n  \"olive oil\":3,\n  \"skim milk\":2,\n  \"eggs\":9,\n  \"flour\":12,\n  \"margarine\":2,\n  \"fresh ginger\":2,\n  \"butter\":15,\n  \"vanilla\":2,\n  \"orange juice\":5,\n  \"whipping cream\":4,\n  \"whole wheat flour\":2,\n  \"lemon\":2,\n  \"sugar\":34,\n  \"salt\":21},\n \"olive oil\":\n {\"elbow macaroni\":17,\n  \"canned beef broth\":6,\n  \"red wine\":109,\n  \"canned tomato sauce\":10,\n  \"bacon\":59,\n  \"brown onions\":2,\n  \"cayenne pepper\":244,\n  \"honey\":210,\n  \"lemon juice\":803,\n  \"zucchini squash\":3,\n  \"swiss cheese\":16,\n  \"paprika\":257,\n  \"bread\":25,\n  \"ground cinnamon\":72,\n  \"dried bay leaf\":2,\n  \"yeast\":145,\n  \"egg yolks\":45,\n  \"green pepper\":223,\n  \"baking powder\":38,\n  \"saffron\":68,\n  \"sesame oil\":37,\n  \"pine nuts\":175,\n  \"cinnamon\":119,\n  \"avocado\":37,\n  \"cheddar cheese\":36,\n  \"corn tortilla\":3,\n  \"tomatoes\":667,\n  \"white pepper\":126,\n  \"brown rice\":27,\n  \"black beans\":59,\n  \"pinto beans\":20,\n  \"fresh garlic\":15,\n  \"cauliflower\":18,\n  \"frozen peas\":26,\n  \"butter\":605,\n  \"salmon\":7,\n  \"orange juice\":86,\n  \"black pepper\":569,\n  \"lemon\":236,\n  \"tumeric\":14,\n  \"black olives\":116,\n  \"buttermilk\":21,\n  \"powdered sugar\":3,\n  \"hoisin sauce\":8,\n  \"eggplant\":191,\n  \"dried oregano\":358,\n  \"capers\":187,\n  \"cornmeal\":66,\n  \"cloves\":68,\n  \"oil\":52,\n  \"chicken\":96,\n  \"blue cheese\":13,\n  \"cucumber\":85,\n  \"dry rosemary\":4,\n  \"flour tortilla\":7,\n  \"canola oil\":13,\n  \"dried tarragon\":46,\n  \"strawberries\":4,\n  \"celery\":123,\n  \"alfalfa sprouts\":9,\n  \"cornish game hens\":6,\n  \"fresh peaches\":3,\n  \"walnuts\":58,\n  \"nutmeg\":107,\n  \"fresh cilantro\":54,\n  \"baking soda\":17,\n  \"bread crumbs\":121,\n  \"corn syrup\":2,\n  \"tabasco sauce\":72,\n  \"ginger\":47,\n  \"balsamic vinegar\":281,\n  \"fresh rosemary\":67,\n  \"lime\":38,\n  \"pecans\":13,\n  \"peanut butter\":8,\n  \"frozen corn\":13,\n  \"green onions\":215,\n  \"cumin\":257,\n  \"canned chicken broth\":4,\n  \"banana\":3,\n  \"skim milk\":38,\n  \"beets\":19,\n  \"barley\":10,\n  \"kidney beans\":35,\n  \"shortening\":2,\n  \"parmesan cheese\":477,\n  \"carrots\":352,\n  \"mushrooms\":272,\n  \"sour cream\":82,\n  \"yogurt\":25,\n  \"brussels sprouts\":2,\n  \"tofu\":21,\n  \"cream cheese\":20,\n  \"cherry tomatoes\":66,\n  \"salsa\":30,\n  \"parsley\":532,\n  \"green olives\":47,\n  \"rice vinegar\":26,\n  \"molasses\":31,\n  \"chili powder\":232,\n  \"beer\":33,\n  \"spaghetti\":77,\n  \"raisins\":74,\n  \"red wine vinegar\":390,\n  \"garam masala\":4,\n  \"chives\":58,\n  \"cornstarch\":95,\n  \"orange\":35,\n  \"dried basil\":239,\n  \"catsup\":11,\n  \"radishes\":13,\n  \"ground beef\":57,\n  \"romaine lettuce\":14,\n  \"mozzarella cheese\":120,\n  \"sweet red pepper\":53,\n  \"canned tuna\":2,\n  \"sweet red onions\":3,\n  \"cumin seeds\":42,\n  \"barbecue sauce\":6,\n  \"raw spinach\":2,\n  \"red pepper flakes\":124,\n  \"almonds\":19,\n  \"eggs\":322,\n  \"margarine\":52,\n  \"fresh ginger\":22,\n  \"kale\":15,\n  \"garlic powder\":159,\n  \"dried thyme\":234,\n  \"cream of mushroom soup\":3,\n  \"low fat milk\":2,\n  \"whole wheat flour\":58,\n  \"salt\":3604,\n  \"fresh basil\":199,\n  \"bean sprouts\":2,\n  \"shiitake mushrooms\":21,\n  \"potatoes\":190,\n  \"frozen spinach\":13,\n  \"canned apricots\":2,\n  \"celery seeds\":11,\n  \"allspice\":46,\n  \"dijon mustard\":281,\n  \"soy sauce\":164,\n  \"broccoli\":45,\n  \"apples\":13,\n  \"chicken legs\":9,\n  \"yellow mustard\":5,\n  \"maple syrup\":14,\n  \"white rice\":18,\n  \"sweet potatoes\":27,\n  \"red leaf lettuce\":2,\n  \"egg whites\":44,\n  \"worcestershire sauce\":160,\n  \"dried cilantro\":2,\n  \"brown sugar\":88,\n  \"lime juice\":148,\n  \"dry mustard\":108,\n  \"raw shrimp\":11,\n  \"canned vegetable broth\":4,\n  \"white flour\":17,\n  \"blueberries\":2,\n  \"mayonnaise\":52,\n  \"chicken wings\":5,\n  \"vegetable oil\":99,\n  \"fresh thyme\":72,\n  \"flour\":455,\n  \"curry powder\":92,\n  \"vinegar\":149,\n  \"white wine\":239,\n  \"sherry\":35,\n  \"yam\":2,\n  \"oats\":4,\n  \"whipping cream\":67,\n  \"chicken breast\":35,\n  \"sugar\":673},\n \"allspice\":\n {\"red wine\":9,\n  \"canned tomato sauce\":2,\n  \"bacon\":5,\n  \"cayenne pepper\":41,\n  \"honey\":54,\n  \"lemon juice\":42,\n  \"paprika\":46,\n  \"bread\":7,\n  \"ground cinnamon\":19,\n  \"yeast\":13,\n  \"egg yolks\":11,\n  \"green pepper\":15,\n  \"baking powder\":124,\n  \"saffron\":2,\n  \"cream of tartar\":2,\n  \"cinnamon\":411,\n  \"pine nuts\":12,\n  \"cheddar cheese\":8,\n  \"tomatoes\":32,\n  \"white pepper\":12,\n  \"brown rice\":2,\n  \"black beans\":4,\n  \"butter\":98,\n  \"orange juice\":25,\n  \"black pepper\":68,\n  \"lemon\":20,\n  \"tumeric\":9,\n  \"buttermilk\":29,\n  \"powdered sugar\":5,\n  \"eggplant\":4,\n  \"dried oregano\":6,\n  \"capers\":3,\n  \"cornmeal\":4,\n  \"cloves\":179,\n  \"oil\":44,\n  \"chicken\":13,\n  \"cucumber\":4,\n  \"flour tortilla\":2,\n  \"canola oil\":4,\n  \"dried tarragon\":2,\n  \"strawberries\":2,\n  \"celery\":12,\n  \"cornish game hens\":2,\n  \"walnuts\":24,\n  \"nutmeg\":231,\n  \"baking soda\":139,\n  \"bread crumbs\":12,\n  \"corn syrup\":4,\n  \"tabasco sauce\":11,\n  \"ginger\":93,\n  \"balsamic vinegar\":5,\n  \"lime\":6,\n  \"pecans\":23,\n  \"peanut butter\":4,\n  \"green onions\":7,\n  \"cumin\":39,\n  \"beets\":4,\n  \"banana\":2,\n  \"skim milk\":6,\n  \"kidney beans\":3,\n  \"shortening\":44,\n  \"parmesan cheese\":2,\n  \"applesauce\":29,\n  \"carrots\":16,\n  \"mushrooms\":5,\n  \"sour cream\":7,\n  \"yogurt\":4,\n  \"cream cheese\":4,\n  \"tofu\":3,\n  \"cherry tomatoes\":2,\n  \"parsley\":20,\n  \"green olives\":2,\n  \"molasses\":51,\n  \"chili powder\":38,\n  \"beer\":9,\n  \"spaghetti\":4,\n  \"raisins\":103,\n  \"red wine vinegar\":8,\n  \"garam masala\":2,\n  \"chives\":2,\n  \"cornstarch\":18,\n  \"dried basil\":4,\n  \"orange\":9,\n  \"catsup\":8,\n  \"ground beef\":23,\n  \"sweet red pepper\":2,\n  \"cumin seeds\":2,\n  \"red pepper flakes\":3,\n  \"almonds\":22,\n  \"eggs\":149,\n  \"margarine\":25,\n  \"fresh ginger\":3,\n  \"kale\":2,\n  \"garlic powder\":22,\n  \"dried thyme\":15,\n  \"cream of mushroom soup\":2,\n  \"vanilla\":73,\n  \"whole wheat flour\":21,\n  \"salt\":443,\n  \"fresh basil\":2,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":12,\n  \"almond extract\":3,\n  \"celery seeds\":2,\n  \"dijon mustard\":5,\n  \"soy sauce\":23,\n  \"broccoli\":3,\n  \"apples\":24,\n  \"yellow mustard\":2,\n  \"maple syrup\":9,\n  \"sweet potatoes\":8,\n  \"egg whites\":13,\n  \"worcestershire sauce\":16,\n  \"dried cilantro\":2,\n  \"brown sugar\":137,\n  \"lime juice\":7,\n  \"dry mustard\":22,\n  \"white flour\":6,\n  \"blueberries\":3,\n  \"mayonnaise\":5,\n  \"chicken wings\":4,\n  \"vegetable oil\":45,\n  \"olive oil\":46,\n  \"fresh thyme\":3,\n  \"flour\":165,\n  \"curry powder\":13,\n  \"vinegar\":64,\n  \"white wine\":8,\n  \"sherry\":3,\n  \"oats\":6,\n  \"whipping cream\":5,\n  \"chicken breast\":3,\n  \"sugar\":264},\n \"strawberries\":\n {\"sour cream\":15,\n  \"yogurt\":9,\n  \"red wine\":3,\n  \"tofu\":2,\n  \"cream cheese\":21,\n  \"salsa\":2,\n  \"cayenne pepper\":2,\n  \"rice vinegar\":2,\n  \"lemon juice\":71,\n  \"honey\":30,\n  \"paprika\":2,\n  \"chili powder\":2,\n  \"ground cinnamon\":5,\n  \"raisins\":3,\n  \"yeast\":4,\n  \"egg yolks\":27,\n  \"baking powder\":41,\n  \"cornstarch\":39,\n  \"orange\":13,\n  \"pine nuts\":3,\n  \"cream of tartar\":9,\n  \"cinnamon\":22,\n  \"avocado\":4,\n  \"white pepper\":2,\n  \"red pepper flakes\":2,\n  \"eggs\":48,\n  \"almonds\":4,\n  \"margarine\":13,\n  \"butter\":52,\n  \"vanilla\":53,\n  \"orange juice\":49,\n  \"whole wheat flour\":2,\n  \"black pepper\":2,\n  \"lemon\":12,\n  \"salt\":78,\n  \"buttermilk\":8,\n  \"powdered sugar\":16,\n  \"almond extract\":6,\n  \"cornmeal\":2,\n  \"cloves\":3,\n  \"oil\":5,\n  \"allspice\":2,\n  \"canola oil\":2,\n  \"dijon mustard\":3,\n  \"apples\":6,\n  \"maple syrup\":3,\n  \"nutmeg\":14,\n  \"walnuts\":8,\n  \"egg whites\":29,\n  \"baking soda\":19,\n  \"worcestershire sauce\":2,\n  \"corn syrup\":3,\n  \"brown sugar\":11,\n  \"balsamic vinegar\":8,\n  \"lime juice\":6,\n  \"lime\":5,\n  \"pecans\":6,\n  \"blueberries\":38,\n  \"peanut butter\":3,\n  \"mayonnaise\":2,\n  \"green onions\":2,\n  \"olive oil\":4,\n  \"vegetable oil\":9,\n  \"skim milk\":5,\n  \"banana\":24,\n  \"flour\":44,\n  \"vinegar\":3,\n  \"shortening\":6,\n  \"applesauce\":4,\n  \"white wine\":8,\n  \"carrots\":2,\n  \"sherry\":6,\n  \"oats\":2,\n  \"whipping cream\":43,\n  \"sugar\":269},\n \"red pepper flakes\":\n {\"red wine\":6,\n  \"bacon\":7,\n  \"cayenne pepper\":8,\n  \"lemon juice\":27,\n  \"honey\":19,\n  \"paprika\":18,\n  \"ground cinnamon\":3,\n  \"egg yolks\":4,\n  \"green pepper\":13,\n  \"baking powder\":2,\n  \"saffron\":2,\n  \"sesame oil\":15,\n  \"cinnamon\":4,\n  \"pine nuts\":4,\n  \"avocado\":2,\n  \"cheddar cheese\":2,\n  \"tomatoes\":26,\n  \"white pepper\":6,\n  \"brown rice\":2,\n  \"black beans\":6,\n  \"pinto beans\":3,\n  \"cauliflower\":3,\n  \"butter\":19,\n  \"frozen peas\":5,\n  \"salmon\":2,\n  \"orange juice\":5,\n  \"lemon\":4,\n  \"black pepper\":35,\n  \"tumeric\":2,\n  \"black olives\":4,\n  \"hoisin sauce\":3,\n  \"dried oregano\":17,\n  \"eggplant\":6,\n  \"capers\":8,\n  \"cornmeal\":3,\n  \"cloves\":4,\n  \"oil\":10,\n  \"chicken\":4,\n  \"cucumber\":5,\n  \"canola oil\":2,\n  \"strawberries\":2,\n  \"celery\":11,\n  \"walnuts\":2,\n  \"nutmeg\":5,\n  \"fresh cilantro\":5,\n  \"bread crumbs\":2,\n  \"baking soda\":2,\n  \"tabasco sauce\":11,\n  \"ginger\":11,\n  \"balsamic vinegar\":9,\n  \"fresh rosemary\":2,\n  \"lime\":5,\n  \"peanut butter\":2,\n  \"cumin\":16,\n  \"green onions\":18,\n  \"frozen corn\":3,\n  \"canned chicken broth\":2,\n  \"kidney beans\":6,\n  \"parmesan cheese\":16,\n  \"carrots\":20,\n  \"mushrooms\":11,\n  \"sour cream\":6,\n  \"tofu\":2,\n  \"cream cheese\":2,\n  \"cherry tomatoes\":3,\n  \"salsa\":2,\n  \"parsley\":25,\n  \"rice vinegar\":13,\n  \"molasses\":4,\n  \"chili powder\":21,\n  \"beer\":5,\n  \"spaghetti\":7,\n  \"raisins\":4,\n  \"red wine vinegar\":14,\n  \"cornstarch\":16,\n  \"orange\":2,\n  \"dried basil\":20,\n  \"catsup\":2,\n  \"ground beef\":7,\n  \"mozzarella cheese\":8,\n  \"sweet red pepper\":3,\n  \"barbecue sauce\":2,\n  \"eggs\":10,\n  \"margarine\":3,\n  \"fresh ginger\":4,\n  \"kale\":2,\n  \"garlic powder\":13,\n  \"dried thyme\":17,\n  \"salt\":149,\n  \"fresh basil\":9,\n  \"bean sprouts\":4,\n  \"shiitake mushrooms\":3,\n  \"potatoes\":4,\n  \"allspice\":3,\n  \"dijon mustard\":6,\n  \"soy sauce\":37,\n  \"broccoli\":7,\n  \"white rice\":3,\n  \"yellow mustard\":3,\n  \"maple syrup\":5,\n  \"egg whites\":4,\n  \"worcestershire sauce\":8,\n  \"dried cilantro\":2,\n  \"brown sugar\":21,\n  \"lime juice\":9,\n  \"dry mustard\":5,\n  \"raw shrimp\":3,\n  \"mayonnaise\":3,\n  \"chicken wings\":4,\n  \"olive oil\":124,\n  \"fresh thyme\":3,\n  \"vegetable oil\":25,\n  \"flour\":11,\n  \"curry powder\":10,\n  \"vinegar\":11,\n  \"white wine\":8,\n  \"sherry\":3,\n  \"whipping cream\":2,\n  \"chicken breast\":2,\n  \"sugar\":45},\n \"corn tortilla\":\n {\"sour cream\":4,\n  \"brown sugar\":2,\n  \"ground beef\":2,\n  \"lime juice\":2,\n  \"avocado\":2,\n  \"cheddar cheese\":5,\n  \"lime\":3,\n  \"tomatoes\":2,\n  \"cumin\":2,\n  \"oil\":4,\n  \"chicken\":2,\n  \"vegetable oil\":3,\n  \"olive oil\":3,\n  \"swiss cheese\":2,\n  \"flour\":2,\n  \"eggs\":2,\n  \"chili powder\":2,\n  \"almonds\":2,\n  \"ground cinnamon\":6,\n  \"raisins\":3,\n  \"salt\":8},\n \"broccoli\":\n {\"red wine\":2,\n  \"bacon\":8,\n  \"cayenne pepper\":2,\n  \"lemon juice\":16,\n  \"honey\":8,\n  \"swiss cheese\":5,\n  \"paprika\":8,\n  \"ground cinnamon\":2,\n  \"egg yolks\":2,\n  \"green pepper\":10,\n  \"baking powder\":3,\n  \"sesame oil\":18,\n  \"pine nuts\":4,\n  \"cream of tartar\":2,\n  \"cinnamon\":2,\n  \"avocado\":2,\n  \"cheddar cheese\":21,\n  \"tomatoes\":14,\n  \"white pepper\":8,\n  \"brown rice\":5,\n  \"cauliflower\":30,\n  \"butter\":40,\n  \"salmon\":2,\n  \"orange juice\":5,\n  \"black pepper\":13,\n  \"lemon\":4,\n  \"black olives\":2,\n  \"hoisin sauce\":2,\n  \"dried oregano\":7,\n  \"eggplant\":2,\n  \"capers\":2,\n  \"oil\":29,\n  \"chicken\":11,\n  \"blue cheese\":2,\n  \"canola oil\":3,\n  \"celery\":20,\n  \"alfalfa sprouts\":2,\n  \"nutmeg\":9,\n  \"walnuts\":4,\n  \"bread crumbs\":3,\n  \"ginger\":5,\n  \"balsamic vinegar\":2,\n  \"pecans\":4,\n  \"green onions\":12,\n  \"cumin\":3,\n  \"skim milk\":8,\n  \"banana\":2,\n  \"parmesan cheese\":20,\n  \"carrots\":48,\n  \"mushrooms\":33,\n  \"sour cream\":4,\n  \"yogurt\":4,\n  \"brussels sprouts\":2,\n  \"tofu\":8,\n  \"cream cheese\":3,\n  \"cherry tomatoes\":4,\n  \"parsley\":12,\n  \"green olives\":3,\n  \"rice vinegar\":4,\n  \"beer\":3,\n  \"chili powder\":3,\n  \"spaghetti\":4,\n  \"raisins\":3,\n  \"red wine vinegar\":8,\n  \"garam masala\":2,\n  \"chives\":4,\n  \"cornstarch\":35,\n  \"dried basil\":5,\n  \"radishes\":2,\n  \"romaine lettuce\":2,\n  \"mozzarella cheese\":5,\n  \"sweet red pepper\":3,\n  \"red pepper flakes\":7,\n  \"eggs\":20,\n  \"margarine\":10,\n  \"fresh ginger\":3,\n  \"garlic powder\":12,\n  \"dried thyme\":4,\n  \"cream of mushroom soup\":4,\n  \"salt\":111,\n  \"fresh basil\":4,\n  \"bean sprouts\":3,\n  \"potatoes\":10,\n  \"frozen spinach\":2,\n  \"allspice\":3,\n  \"dijon mustard\":7,\n  \"soy sauce\":34,\n  \"sweet potatoes\":3,\n  \"worcestershire sauce\":2,\n  \"brown sugar\":3,\n  \"lime juice\":2,\n  \"dry mustard\":7,\n  \"mayonnaise\":14,\n  \"olive oil\":45,\n  \"vegetable oil\":29,\n  \"flour\":35,\n  \"curry powder\":9,\n  \"vinegar\":10,\n  \"white wine\":4,\n  \"sherry\":6,\n  \"chicken breast\":3,\n  \"sugar\":30},\n \"low fat milk\":\n {\"cream cheese\":2,\n  \"buttermilk\":2,\n  \"canned tomato sauce\":2,\n  \"almond extract\":2,\n  \"parsley\":2,\n  \"oil\":2,\n  \"lemon juice\":2,\n  \"honey\":2,\n  \"ground cinnamon\":3,\n  \"raisins\":4,\n  \"celery\":2,\n  \"nutmeg\":2,\n  \"baking soda\":3,\n  \"baking powder\":2,\n  \"worcestershire sauce\":2,\n  \"cornstarch\":2,\n  \"tabasco sauce\":2,\n  \"catsup\":2,\n  \"brown sugar\":2,\n  \"cinnamon\":4,\n  \"lime juice\":2,\n  \"olive oil\":2,\n  \"white pepper\":2,\n  \"vegetable oil\":3,\n  \"eggs\":8,\n  \"flour\":7,\n  \"curry powder\":2,\n  \"margarine\":7,\n  \"butter\":4,\n  \"garlic powder\":2,\n  \"dried thyme\":2,\n  \"parmesan cheese\":3,\n  \"orange juice\":2,\n  \"whole wheat flour\":2,\n  \"sugar\":3,\n  \"salt\":10},\n \"powdered sugar\":\n {\"sour cream\":52,\n  \"yogurt\":3,\n  \"cream cheese\":153,\n  \"bacon\":2,\n  \"honey\":17,\n  \"lemon juice\":92,\n  \"molasses\":14,\n  \"paprika\":2,\n  \"bread\":5,\n  \"ground cinnamon\":71,\n  \"raisins\":40,\n  \"yeast\":15,\n  \"egg yolks\":74,\n  \"baking powder\":219,\n  \"cornstarch\":47,\n  \"orange\":6,\n  \"catsup\":2,\n  \"cream of tartar\":49,\n  \"cinnamon\":126,\n  \"pine nuts\":4,\n  \"white pepper\":2,\n  \"almonds\":49,\n  \"eggs\":365,\n  \"margarine\":138,\n  \"butter\":421,\n  \"salmon\":2,\n  \"orange juice\":39,\n  \"vanilla\":430,\n  \"whole wheat flour\":5,\n  \"lemon\":14,\n  \"salt\":468,\n  \"black pepper\":4,\n  \"potatoes\":2,\n  \"buttermilk\":21,\n  \"almond extract\":61,\n  \"cloves\":13,\n  \"oil\":36,\n  \"canola oil\":3,\n  \"allspice\":5,\n  \"dijon mustard\":2,\n  \"soy sauce\":3,\n  \"strawberries\":16,\n  \"apples\":17,\n  \"maple syrup\":13,\n  \"fresh peaches\":2,\n  \"nutmeg\":42,\n  \"walnuts\":50,\n  \"egg whites\":124,\n  \"baking soda\":149,\n  \"worcestershire sauce\":3,\n  \"corn syrup\":4,\n  \"brown sugar\":69,\n  \"ginger\":7,\n  \"lime juice\":7,\n  \"lime\":3,\n  \"white flour\":2,\n  \"pecans\":55,\n  \"blueberries\":7,\n  \"mayonnaise\":7,\n  \"peanut butter\":53,\n  \"cumin\":2,\n  \"olive oil\":3,\n  \"vegetable oil\":49,\n  \"beets\":3,\n  \"skim milk\":27,\n  \"banana\":7,\n  \"flour\":341,\n  \"shortening\":53,\n  \"vinegar\":5,\n  \"white wine\":8,\n  \"applesauce\":12,\n  \"sherry\":3,\n  \"carrots\":14,\n  \"oats\":12,\n  \"whipping cream\":89,\n  \"chicken breast\":2,\n  \"sugar\":496},\n \"dijon mustard\":\n {\"elbow macaroni\":3,\n  \"red wine\":7,\n  \"bacon\":21,\n  \"cayenne pepper\":33,\n  \"lemon juice\":150,\n  \"honey\":149,\n  \"zucchini squash\":2,\n  \"swiss cheese\":12,\n  \"paprika\":40,\n  \"bread\":3,\n  \"ground cinnamon\":2,\n  \"yeast\":6,\n  \"egg yolks\":19,\n  \"green pepper\":17,\n  \"baking powder\":3,\n  \"sesame oil\":14,\n  \"cinnamon\":7,\n  \"pine nuts\":7,\n  \"avocado\":3,\n  \"cheddar cheese\":11,\n  \"tomatoes\":23,\n  \"white pepper\":28,\n  \"brown rice\":2,\n  \"fresh garlic\":2,\n  \"pinto beans\":2,\n  \"cauliflower\":4,\n  \"frozen peas\":5,\n  \"butter\":131,\n  \"orange juice\":26,\n  \"black pepper\":76,\n  \"lemon\":21,\n  \"black olives\":3,\n  \"buttermilk\":10,\n  \"powdered sugar\":2,\n  \"eggplant\":2,\n  \"dried oregano\":25,\n  \"hoisin sauce\":4,\n  \"capers\":24,\n  \"cornmeal\":2,\n  \"cloves\":4,\n  \"oil\":33,\n  \"chicken\":14,\n  \"blue cheese\":5,\n  \"cucumber\":12,\n  \"dry rosemary\":3,\n  \"canola oil\":11,\n  \"dried tarragon\":33,\n  \"strawberries\":3,\n  \"celery\":23,\n  \"cornish game hens\":3,\n  \"alfalfa sprouts\":6,\n  \"fresh peaches\":2,\n  \"fresh cilantro\":2,\n  \"walnuts\":13,\n  \"nutmeg\":16,\n  \"bread crumbs\":19,\n  \"tabasco sauce\":20,\n  \"corn syrup\":3,\n  \"ginger\":5,\n  \"balsamic vinegar\":56,\n  \"fresh rosemary\":8,\n  \"lime\":3,\n  \"pecans\":4,\n  \"frozen corn\":3,\n  \"green onions\":39,\n  \"cumin\":13,\n  \"skim milk\":8,\n  \"beets\":5,\n  \"barley\":2,\n  \"parmesan cheese\":24,\n  \"carrots\":34,\n  \"mushrooms\":27,\n  \"sour cream\":50,\n  \"yogurt\":6,\n  \"brussels sprouts\":4,\n  \"cream cheese\":16,\n  \"tofu\":3,\n  \"cherry tomatoes\":7,\n  \"salsa\":7,\n  \"parsley\":50,\n  \"green olives\":2,\n  \"rice vinegar\":15,\n  \"molasses\":19,\n  \"chili powder\":22,\n  \"beer\":12,\n  \"raisins\":7,\n  \"red wine vinegar\":100,\n  \"garam masala\":2,\n  \"chives\":17,\n  \"cornstarch\":34,\n  \"dried basil\":24,\n  \"radishes\":4,\n  \"catsup\":6,\n  \"ground beef\":6,\n  \"romaine lettuce\":3,\n  \"mozzarella cheese\":5,\n  \"sweet red pepper\":10,\n  \"cumin seeds\":2,\n  \"barbecue sauce\":4,\n  \"red pepper flakes\":6,\n  \"almonds\":3,\n  \"eggs\":56,\n  \"margarine\":16,\n  \"fresh ginger\":4,\n  \"kale\":3,\n  \"garlic powder\":33,\n  \"dried thyme\":41,\n  \"cream of mushroom soup\":2,\n  \"whole wheat flour\":5,\n  \"salt\":346,\n  \"fresh basil\":17,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":8,\n  \"canned apricots\":2,\n  \"celery seeds\":4,\n  \"allspice\":5,\n  \"soy sauce\":64,\n  \"broccoli\":7,\n  \"apples\":6,\n  \"yellow mustard\":3,\n  \"sweet potatoes\":4,\n  \"maple syrup\":15,\n  \"egg whites\":5,\n  \"worcestershire sauce\":96,\n  \"brown sugar\":44,\n  \"lime juice\":22,\n  \"dry mustard\":28,\n  \"raw shrimp\":3,\n  \"white flour\":2,\n  \"mayonnaise\":98,\n  \"chicken wings\":5,\n  \"olive oil\":281,\n  \"vegetable oil\":114,\n  \"fresh thyme\":10,\n  \"curry powder\":21,\n  \"flour\":61,\n  \"vinegar\":26,\n  \"white wine\":22,\n  \"sherry\":5,\n  \"yam\":2,\n  \"whipping cream\":33,\n  \"chicken breast\":6,\n  \"sugar\":83},\n \"parsley\":\n {\"elbow macaroni\":7,\n  \"red wine\":32,\n  \"canned beef broth\":2,\n  \"canned tomato sauce\":2,\n  \"bacon\":75,\n  \"cayenne pepper\":85,\n  \"lemon juice\":300,\n  \"honey\":27,\n  \"zucchini squash\":2,\n  \"swiss cheese\":22,\n  \"paprika\":166,\n  \"ground cinnamon\":4,\n  \"bread\":29,\n  \"yeast\":18,\n  \"egg yolks\":35,\n  \"green pepper\":104,\n  \"baking powder\":21,\n  \"saffron\":18,\n  \"sesame oil\":8,\n  \"pine nuts\":30,\n  \"cinnamon\":36,\n  \"avocado\":6,\n  \"cheddar cheese\":48,\n  \"tomatoes\":245,\n  \"white pepper\":52,\n  \"brown rice\":21,\n  \"black beans\":9,\n  \"fresh garlic\":2,\n  \"pinto beans\":6,\n  \"cauliflower\":9,\n  \"frozen peas\":10,\n  \"butter\":586,\n  \"salmon\":14,\n  \"orange juice\":13,\n  \"black pepper\":207,\n  \"lemon\":94,\n  \"tumeric\":3,\n  \"black olives\":26,\n  \"buttermilk\":11,\n  \"hoisin sauce\":2,\n  \"dried oregano\":28,\n  \"eggplant\":34,\n  \"capers\":50,\n  \"cornmeal\":10,\n  \"cloves\":31,\n  \"oil\":213,\n  \"chicken\":70,\n  \"blue cheese\":9,\n  \"cucumber\":30,\n  \"flour tortilla\":2,\n  \"canola oil\":9,\n  \"dried tarragon\":7,\n  \"celery\":251,\n  \"cornish game hens\":2,\n  \"alfalfa sprouts\":4,\n  \"fresh cilantro\":9,\n  \"walnuts\":27,\n  \"nutmeg\":52,\n  \"baking soda\":4,\n  \"bread crumbs\":145,\n  \"tabasco sauce\":41,\n  \"corn syrup\":3,\n  \"balsamic vinegar\":24,\n  \"ginger\":25,\n  \"fresh rosemary\":4,\n  \"lime\":7,\n  \"pecans\":14,\n  \"peanut butter\":4,\n  \"frozen corn\":3,\n  \"green onions\":148,\n  \"cumin\":79,\n  \"banana\":2,\n  \"skim milk\":12,\n  \"beets\":6,\n  \"barley\":18,\n  \"kidney beans\":12,\n  \"shortening\":10,\n  \"applesauce\":2,\n  \"parmesan cheese\":182,\n  \"carrots\":189,\n  \"mushrooms\":213,\n  \"sour cream\":82,\n  \"yogurt\":20,\n  \"brussels sprouts\":3,\n  \"tofu\":11,\n  \"cream cheese\":28,\n  \"cherry tomatoes\":11,\n  \"green olives\":23,\n  \"rice vinegar\":3,\n  \"molasses\":4,\n  \"chili powder\":48,\n  \"beer\":16,\n  \"spaghetti\":31,\n  \"raisins\":23,\n  \"red wine vinegar\":48,\n  \"chives\":76,\n  \"cornstarch\":46,\n  \"dried basil\":34,\n  \"orange\":10,\n  \"catsup\":12,\n  \"radishes\":5,\n  \"ground beef\":65,\n  \"romaine lettuce\":2,\n  \"adzuki beans\":3,\n  \"mozzarella cheese\":40,\n  \"sweet red pepper\":7,\n  \"cumin seeds\":4,\n  \"barbecue sauce\":6,\n  \"raw spinach\":2,\n  \"red pepper flakes\":25,\n  \"almonds\":19,\n  \"eggs\":232,\n  \"margarine\":84,\n  \"fresh ginger\":6,\n  \"kale\":3,\n  \"garlic powder\":78,\n  \"dried thyme\":38,\n  \"cream of mushroom soup\":17,\n  \"low fat milk\":2,\n  \"whole wheat flour\":7,\n  \"salt\":1139,\n  \"fresh basil\":16,\n  \"shiitake mushrooms\":4,\n  \"potatoes\":139,\n  \"frozen spinach\":6,\n  \"allspice\":20,\n  \"dijon mustard\":50,\n  \"soy sauce\":59,\n  \"broccoli\":12,\n  \"apples\":10,\n  \"shell macaroni\":3,\n  \"yellow mustard\":2,\n  \"white rice\":6,\n  \"sweet potatoes\":8,\n  \"maple syrup\":2,\n  \"egg whites\":14,\n  \"worcestershire sauce\":118,\n  \"brown sugar\":30,\n  \"lime juice\":17,\n  \"dry mustard\":49,\n  \"raw shrimp\":5,\n  \"white flour\":3,\n  \"mayonnaise\":72,\n  \"chicken wings\":4,\n  \"fresh thyme\":14,\n  \"vegetable oil\":117,\n  \"olive oil\":532,\n  \"curry powder\":39,\n  \"flour\":362,\n  \"vinegar\":68,\n  \"white wine\":99,\n  \"sherry\":27,\n  \"oats\":7,\n  \"whipping cream\":18,\n  \"chicken breast\":5,\n  \"sugar\":163},\n \"orange juice\":\n {\"red wine\":8,\n  \"bacon\":7,\n  \"cayenne pepper\":20,\n  \"lemon juice\":275,\n  \"honey\":164,\n  \"paprika\":28,\n  \"ground cinnamon\":72,\n  \"bread\":7,\n  \"yeast\":12,\n  \"egg yolks\":29,\n  \"green pepper\":10,\n  \"baking powder\":211,\n  \"sesame oil\":13,\n  \"cinnamon\":178,\n  \"cream of tartar\":15,\n  \"pine nuts\":3,\n  \"avocado\":2,\n  \"cheddar cheese\":2,\n  \"tomatoes\":14,\n  \"white pepper\":9,\n  \"brown rice\":2,\n  \"black beans\":3,\n  \"fresh garlic\":2,\n  \"frozen peas\":2,\n  \"butter\":222,\n  \"black pepper\":29,\n  \"lemon\":27,\n  \"buttermilk\":16,\n  \"powdered sugar\":39,\n  \"hoisin sauce\":7,\n  \"dried oregano\":8,\n  \"capers\":4,\n  \"cornmeal\":3,\n  \"cloves\":30,\n  \"oil\":52,\n  \"chicken\":15,\n  \"blue cheese\":2,\n  \"cucumber\":3,\n  \"canola oil\":20,\n  \"dried tarragon\":3,\n  \"strawberries\":49,\n  \"celery\":16,\n  \"cornish game hens\":4,\n  \"alfalfa sprouts\":2,\n  \"fresh cilantro\":7,\n  \"walnuts\":29,\n  \"nutmeg\":69,\n  \"fresh peaches\":5,\n  \"baking soda\":137,\n  \"bread crumbs\":6,\n  \"tabasco sauce\":7,\n  \"corn syrup\":7,\n  \"balsamic vinegar\":14,\n  \"ginger\":47,\n  \"fresh rosemary\":7,\n  \"lime\":9,\n  \"pecans\":23,\n  \"peanut butter\":6,\n  \"green onions\":32,\n  \"cumin\":13,\n  \"skim milk\":19,\n  \"banana\":35,\n  \"beets\":4,\n  \"barley\":3,\n  \"shortening\":27,\n  \"applesauce\":9,\n  \"carrots\":54,\n  \"mushrooms\":7,\n  \"sour cream\":22,\n  \"yogurt\":7,\n  \"tofu\":3,\n  \"cream cheese\":29,\n  \"salsa\":3,\n  \"parsley\":13,\n  \"green olives\":3,\n  \"rice vinegar\":5,\n  \"molasses\":29,\n  \"beer\":2,\n  \"chili powder\":15,\n  \"spaghetti\":2,\n  \"raisins\":105,\n  \"red wine vinegar\":18,\n  \"chives\":3,\n  \"cornstarch\":140,\n  \"dried basil\":7,\n  \"orange\":64,\n  \"catsup\":5,\n  \"radishes\":3,\n  \"ground beef\":2,\n  \"sweet red pepper\":3,\n  \"cumin seeds\":4,\n  \"barbecue sauce\":3,\n  \"red pepper flakes\":5,\n  \"almonds\":10,\n  \"eggs\":197,\n  \"margarine\":64,\n  \"fresh ginger\":7,\n  \"garlic powder\":19,\n  \"dried thyme\":15,\n  \"cream of mushroom soup\":2,\n  \"low fat milk\":2,\n  \"vanilla\":97,\n  \"whole wheat flour\":30,\n  \"salt\":548,\n  \"fresh basil\":2,\n  \"bean sprouts\":2,\n  \"potatoes\":10,\n  \"almond extract\":21,\n  \"allspice\":25,\n  \"dijon mustard\":26,\n  \"soy sauce\":77,\n  \"broccoli\":5,\n  \"chicken legs\":2,\n  \"apples\":33,\n  \"yellow mustard\":3,\n  \"sweet potatoes\":28,\n  \"maple syrup\":18,\n  \"egg whites\":59,\n  \"worcestershire sauce\":9,\n  \"brown sugar\":135,\n  \"lime juice\":48,\n  \"dry mustard\":19,\n  \"white flour\":3,\n  \"blueberries\":20,\n  \"mayonnaise\":9,\n  \"chicken wings\":4,\n  \"fresh thyme\":3,\n  \"olive oil\":86,\n  \"vegetable oil\":90,\n  \"curry powder\":28,\n  \"flour\":199,\n  \"vinegar\":32,\n  \"white wine\":19,\n  \"sherry\":17,\n  \"oats\":3,\n  \"whipping cream\":28,\n  \"chicken breast\":6,\n  \"sugar\":633},\n \"eggs\":\n {\"elbow macaroni\":11,\n  \"red wine\":14,\n  \"canned tomato sauce\":2,\n  \"bacon\":157,\n  \"brown onions\":2,\n  \"cayenne pepper\":86,\n  \"lemon juice\":525,\n  \"honey\":271,\n  \"zucchini squash\":2,\n  \"swiss cheese\":84,\n  \"paprika\":151,\n  \"camembert cheese\":2,\n  \"ground cinnamon\":428,\n  \"bread\":85,\n  \"yeast\":166,\n  \"egg yolks\":148,\n  \"green pepper\":97,\n  \"baking powder\":2558,\n  \"saffron\":8,\n  \"sesame oil\":51,\n  \"pine nuts\":33,\n  \"cream of tartar\":141,\n  \"cinnamon\":1239,\n  \"avocado\":10,\n  \"cheddar cheese\":218,\n  \"corn tortilla\":2,\n  \"tomatoes\":126,\n  \"white pepper\":97,\n  \"brown rice\":12,\n  \"black beans\":5,\n  \"fresh garlic\":3,\n  \"cauliflower\":12,\n  \"frozen peas\":16,\n  \"butter\":2909,\n  \"salmon\":20,\n  \"orange juice\":197,\n  \"black pepper\":176,\n  \"lemon\":138,\n  \"tumeric\":2,\n  \"black olives\":22,\n  \"buttermilk\":430,\n  \"powdered sugar\":365,\n  \"hoisin sauce\":7,\n  \"eggplant\":38,\n  \"dried oregano\":51,\n  \"capers\":14,\n  \"cornmeal\":107,\n  \"cloves\":202,\n  \"oil\":748,\n  \"chicken\":46,\n  \"blue cheese\":4,\n  \"cucumber\":11,\n  \"flour tortilla\":7,\n  \"canola oil\":50,\n  \"dried tarragon\":13,\n  \"strawberries\":48,\n  \"celery\":104,\n  \"alfalfa sprouts\":5,\n  \"fresh cilantro\":7,\n  \"fresh peaches\":9,\n  \"walnuts\":326,\n  \"nutmeg\":697,\n  \"baking soda\":1900,\n  \"bread crumbs\":192,\n  \"tabasco sauce\":41,\n  \"corn syrup\":49,\n  \"balsamic vinegar\":7,\n  \"ginger\":151,\n  \"fresh rosemary\":5,\n  \"lime\":3,\n  \"pecans\":304,\n  \"peanut butter\":110,\n  \"frozen corn\":8,\n  \"cumin\":26,\n  \"green onions\":149,\n  \"canned chicken broth\":2,\n  \"beets\":13,\n  \"banana\":18,\n  \"skim milk\":90,\n  \"shortening\":538,\n  \"parmesan cheese\":209,\n  \"applesauce\":81,\n  \"carrots\":119,\n  \"mushrooms\":113,\n  \"sour cream\":564,\n  \"yogurt\":29,\n  \"tofu\":10,\n  \"cream cheese\":454,\n  \"cherry tomatoes\":7,\n  \"salsa\":30,\n  \"parsley\":232,\n  \"green olives\":10,\n  \"rice vinegar\":10,\n  \"molasses\":125,\n  \"chili powder\":48,\n  \"beer\":24,\n  \"spaghetti\":22,\n  \"raisins\":559,\n  \"red wine vinegar\":14,\n  \"garam masala\":4,\n  \"chives\":40,\n  \"cornstarch\":359,\n  \"orange\":65,\n  \"dried basil\":50,\n  \"radishes\":2,\n  \"catsup\":26,\n  \"ground beef\":169,\n  \"mozzarella cheese\":94,\n  \"sweet red pepper\":11,\n  \"canned tuna\":2,\n  \"cumin seeds\":5,\n  \"barbecue sauce\":3,\n  \"raw spinach\":4,\n  \"red pepper flakes\":10,\n  \"almonds\":132,\n  \"margarine\":474,\n  \"fresh ginger\":5,\n  \"kale\":3,\n  \"garlic powder\":101,\n  \"dried thyme\":44,\n  \"cream of mushroom soup\":38,\n  \"low fat milk\":8,\n  \"vanilla\":2594,\n  \"whole wheat flour\":135,\n  \"salt\":6373,\n  \"fresh basil\":17,\n  \"bean sprouts\":30,\n  \"shiitake mushrooms\":6,\n  \"potatoes\":138,\n  \"almond extract\":230,\n  \"frozen spinach\":22,\n  \"celery seeds\":2,\n  \"allspice\":149,\n  \"dijon mustard\":56,\n  \"soy sauce\":171,\n  \"broccoli\":20,\n  \"apples\":130,\n  \"yellow mustard\":6,\n  \"sweet potatoes\":27,\n  \"maple syrup\":72,\n  \"white rice\":8,\n  \"egg whites\":105,\n  \"worcestershire sauce\":121,\n  \"brown sugar\":845,\n  \"lime juice\":28,\n  \"dry mustard\":159,\n  \"raw shrimp\":5,\n  \"white flour\":33,\n  \"blueberries\":68,\n  \"mayonnaise\":145,\n  \"chicken wings\":7,\n  \"fresh thyme\":9,\n  \"vegetable oil\":597,\n  \"olive oil\":322,\n  \"curry powder\":33,\n  \"flour\":3597,\n  \"vinegar\":114,\n  \"white wine\":33,\n  \"sherry\":56,\n  \"oats\":57,\n  \"whipping cream\":312,\n  \"chicken breast\":13,\n  \"sugar\":5984},\n \"orange\":\n {\"red wine\":6,\n  \"bacon\":3,\n  \"cayenne pepper\":5,\n  \"lemon juice\":34,\n  \"honey\":35,\n  \"swiss cheese\":2,\n  \"paprika\":8,\n  \"ground cinnamon\":25,\n  \"bread\":2,\n  \"yeast\":4,\n  \"egg yolks\":10,\n  \"green pepper\":5,\n  \"baking powder\":45,\n  \"sesame oil\":4,\n  \"cream of tartar\":4,\n  \"cinnamon\":44,\n  \"pine nuts\":5,\n  \"avocado\":4,\n  \"tomatoes\":6,\n  \"white pepper\":5,\n  \"brown rice\":4,\n  \"black beans\":3,\n  \"butter\":60,\n  \"orange juice\":64,\n  \"lemon\":85,\n  \"black pepper\":10,\n  \"buttermilk\":10,\n  \"powdered sugar\":6,\n  \"dried oregano\":3,\n  \"capers\":3,\n  \"cloves\":22,\n  \"oil\":13,\n  \"chicken\":5,\n  \"cucumber\":3,\n  \"canola oil\":4,\n  \"strawberries\":13,\n  \"celery\":5,\n  \"cornish game hens\":3,\n  \"alfalfa sprouts\":2,\n  \"fresh cilantro\":4,\n  \"walnuts\":20,\n  \"nutmeg\":15,\n  \"baking soda\":41,\n  \"bread crumbs\":4,\n  \"tabasco sauce\":4,\n  \"corn syrup\":2,\n  \"balsamic vinegar\":7,\n  \"ginger\":17,\n  \"fresh rosemary\":2,\n  \"lime\":19,\n  \"pecans\":9,\n  \"peanut butter\":2,\n  \"cumin\":6,\n  \"green onions\":9,\n  \"banana\":12,\n  \"skim milk\":5,\n  \"barley\":2,\n  \"shortening\":6,\n  \"applesauce\":2,\n  \"parmesan cheese\":2,\n  \"carrots\":18,\n  \"mushrooms\":3,\n  \"sour cream\":8,\n  \"yogurt\":2,\n  \"tofu\":2,\n  \"cream cheese\":4,\n  \"salsa\":2,\n  \"parsley\":10,\n  \"rice vinegar\":2,\n  \"molasses\":9,\n  \"spaghetti\":2,\n  \"raisins\":41,\n  \"red wine vinegar\":5,\n  \"chives\":4,\n  \"cornstarch\":26,\n  \"catsup\":3,\n  \"radishes\":2,\n  \"cumin seeds\":2,\n  \"red pepper flakes\":2,\n  \"eggs\":65,\n  \"almonds\":6,\n  \"margarine\":15,\n  \"fresh ginger\":2,\n  \"garlic powder\":6,\n  \"dried thyme\":5,\n  \"vanilla\":16,\n  \"whole wheat flour\":7,\n  \"salt\":122,\n  \"shiitake mushrooms\":3,\n  \"potatoes\":2,\n  \"almond extract\":7,\n  \"allspice\":9,\n  \"soy sauce\":13,\n  \"apples\":15,\n  \"maple syrup\":5,\n  \"sweet potatoes\":6,\n  \"egg whites\":9,\n  \"worcestershire sauce\":4,\n  \"brown sugar\":32,\n  \"lime juice\":4,\n  \"dry mustard\":5,\n  \"blueberries\":6,\n  \"mayonnaise\":3,\n  \"vegetable oil\":23,\n  \"fresh thyme\":5,\n  \"olive oil\":35,\n  \"curry powder\":6,\n  \"flour\":38,\n  \"vinegar\":10,\n  \"white wine\":8,\n  \"sherry\":7,\n  \"oats\":3,\n  \"whipping cream\":14,\n  \"sugar\":208},\n \"salmon\":\n {\"sour cream\":9,\n  \"cream cheese\":9,\n  \"bacon\":5,\n  \"cayenne pepper\":3,\n  \"parsley\":14,\n  \"rice vinegar\":4,\n  \"lemon juice\":34,\n  \"honey\":4,\n  \"swiss cheese\":4,\n  \"paprika\":5,\n  \"beer\":2,\n  \"raisins\":2,\n  \"red wine vinegar\":2,\n  \"chives\":5,\n  \"egg yolks\":3,\n  \"green pepper\":5,\n  \"cornstarch\":2,\n  \"cinnamon\":2,\n  \"cheddar cheese\":4,\n  \"tomatoes\":9,\n  \"cumin seeds\":2,\n  \"white pepper\":2,\n  \"brown rice\":2,\n  \"red pepper flakes\":2,\n  \"black beans\":2,\n  \"almonds\":2,\n  \"eggs\":20,\n  \"margarine\":3,\n  \"fresh ginger\":2,\n  \"butter\":26,\n  \"garlic powder\":4,\n  \"dried thyme\":2,\n  \"cream of mushroom soup\":5,\n  \"salt\":40,\n  \"lemon\":6,\n  \"black pepper\":4,\n  \"powdered sugar\":2,\n  \"dried oregano\":2,\n  \"capers\":2,\n  \"cloves\":3,\n  \"oil\":5,\n  \"cucumber\":3,\n  \"soy sauce\":2,\n  \"celery\":7,\n  \"broccoli\":2,\n  \"apples\":2,\n  \"walnuts\":2,\n  \"nutmeg\":3,\n  \"egg whites\":5,\n  \"bread crumbs\":9,\n  \"worcestershire sauce\":6,\n  \"tabasco sauce\":4,\n  \"brown sugar\":2,\n  \"ginger\":2,\n  \"dry mustard\":2,\n  \"lime\":2,\n  \"mayonnaise\":9,\n  \"cumin\":2,\n  \"green onions\":4,\n  \"olive oil\":7,\n  \"vegetable oil\":7,\n  \"skim milk\":3,\n  \"flour\":16,\n  \"shortening\":2,\n  \"vinegar\":2,\n  \"white wine\":5,\n  \"parmesan cheese\":2,\n  \"carrots\":2,\n  \"whipping cream\":4,\n  \"mushrooms\":4,\n  \"sugar\":8},\n \"vinegar\":\n {\"elbow macaroni\":5,\n  \"red wine\":17,\n  \"canned tomato sauce\":2,\n  \"bacon\":97,\n  \"cayenne pepper\":52,\n  \"lemon juice\":131,\n  \"honey\":80,\n  \"swiss cheese\":2,\n  \"paprika\":145,\n  \"bread\":4,\n  \"ground cinnamon\":24,\n  \"yeast\":9,\n  \"egg yolks\":23,\n  \"green pepper\":123,\n  \"baking powder\":23,\n  \"saffron\":7,\n  \"sesame oil\":50,\n  \"pine nuts\":2,\n  \"cinnamon\":116,\n  \"cream of tartar\":8,\n  \"avocado\":10,\n  \"cheddar cheese\":8,\n  \"tomatoes\":114,\n  \"white pepper\":33,\n  \"brown rice\":2,\n  \"black beans\":8,\n  \"pinto beans\":7,\n  \"cauliflower\":12,\n  \"frozen peas\":2,\n  \"butter\":165,\n  \"salmon\":2,\n  \"orange juice\":32,\n  \"lemon\":41,\n  \"black pepper\":121,\n  \"tumeric\":9,\n  \"black olives\":10,\n  \"buttermilk\":14,\n  \"powdered sugar\":5,\n  \"hoisin sauce\":4,\n  \"dried oregano\":16,\n  \"eggplant\":19,\n  \"capers\":29,\n  \"cornmeal\":5,\n  \"cloves\":105,\n  \"oil\":311,\n  \"chicken\":29,\n  \"blue cheese\":6,\n  \"cucumber\":41,\n  \"canola oil\":11,\n  \"dried tarragon\":4,\n  \"strawberries\":3,\n  \"celery\":91,\n  \"alfalfa sprouts\":2,\n  \"cornish game hens\":2,\n  \"fresh cilantro\":2,\n  \"nutmeg\":31,\n  \"walnuts\":13,\n  \"bread crumbs\":13,\n  \"baking soda\":74,\n  \"corn syrup\":8,\n  \"tabasco sauce\":45,\n  \"ginger\":106,\n  \"fresh rosemary\":2,\n  \"lime\":6,\n  \"pecans\":9,\n  \"peanut butter\":11,\n  \"frozen corn\":2,\n  \"green onions\":50,\n  \"cumin\":48,\n  \"canned chicken broth\":2,\n  \"banana\":2,\n  \"skim milk\":5,\n  \"beets\":17,\n  \"barley\":3,\n  \"kidney beans\":23,\n  \"shortening\":42,\n  \"parmesan cheese\":6,\n  \"applesauce\":7,\n  \"carrots\":84,\n  \"mushrooms\":30,\n  \"sour cream\":55,\n  \"yogurt\":6,\n  \"tofu\":7,\n  \"cream cheese\":10,\n  \"cherry tomatoes\":4,\n  \"salsa\":3,\n  \"parsley\":68,\n  \"green olives\":6,\n  \"rice vinegar\":2,\n  \"molasses\":65,\n  \"chili powder\":134,\n  \"beer\":11,\n  \"raisins\":53,\n  \"red wine vinegar\":2,\n  \"garam masala\":5,\n  \"chives\":7,\n  \"cornstarch\":207,\n  \"orange\":10,\n  \"dried basil\":7,\n  \"radishes\":8,\n  \"catsup\":146,\n  \"ground beef\":59,\n  \"sweet red pepper\":9,\n  \"cumin seeds\":5,\n  \"barbecue sauce\":5,\n  \"raw spinach\":2,\n  \"red pepper flakes\":11,\n  \"eggs\":114,\n  \"almonds\":13,\n  \"margarine\":27,\n  \"fresh ginger\":7,\n  \"kale\":2,\n  \"garlic powder\":76,\n  \"dried thyme\":7,\n  \"vanilla\":76,\n  \"whole wheat flour\":2,\n  \"salt\":1359,\n  \"fresh basil\":2,\n  \"bean sprouts\":6,\n  \"potatoes\":52,\n  \"almond extract\":3,\n  \"allspice\":64,\n  \"celery seeds\":15,\n  \"dijon mustard\":26,\n  \"soy sauce\":270,\n  \"broccoli\":10,\n  \"apples\":35,\n  \"white rice\":2,\n  \"yellow mustard\":3,\n  \"sweet potatoes\":6,\n  \"maple syrup\":6,\n  \"egg whites\":31,\n  \"worcestershire sauce\":228,\n  \"dried cilantro\":2,\n  \"brown sugar\":337,\n  \"lime juice\":14,\n  \"dry mustard\":195,\n  \"blueberries\":2,\n  \"mayonnaise\":74,\n  \"chicken wings\":16,\n  \"olive oil\":149,\n  \"vegetable oil\":127,\n  \"fresh thyme\":4,\n  \"flour\":208,\n  \"curry powder\":28,\n  \"white wine\":14,\n  \"sherry\":50,\n  \"whipping cream\":6,\n  \"chicken breast\":12,\n  \"sugar\":1030},\n \"kale\":\n {\"sour cream\":3,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":9,\n  \"bacon\":3,\n  \"dried oregano\":2,\n  \"cornmeal\":2,\n  \"parsley\":3,\n  \"lemon juice\":3,\n  \"oil\":5,\n  \"honey\":2,\n  \"chicken\":2,\n  \"paprika\":2,\n  \"allspice\":2,\n  \"dijon mustard\":3,\n  \"soy sauce\":2,\n  \"celery\":4,\n  \"chives\":2,\n  \"baking powder\":2,\n  \"tabasco sauce\":2,\n  \"sesame oil\":2,\n  \"balsamic vinegar\":2,\n  \"ginger\":3,\n  \"cheddar cheese\":2,\n  \"tomatoes\":2,\n  \"cumin\":3,\n  \"olive oil\":15,\n  \"fresh thyme\":2,\n  \"vegetable oil\":5,\n  \"skim milk\":2,\n  \"red pepper flakes\":2,\n  \"curry powder\":2,\n  \"eggs\":3,\n  \"pinto beans\":2,\n  \"vinegar\":2,\n  \"butter\":7,\n  \"dried thyme\":3,\n  \"parmesan cheese\":5,\n  \"carrots\":3,\n  \"sherry\":2,\n  \"sugar\":5,\n  \"salt\":20,\n  \"black pepper\":4},\n \"white flour\":\n {\"sour cream\":6,\n  \"yogurt\":4,\n  \"tofu\":3,\n  \"cream cheese\":3,\n  \"salsa\":2,\n  \"parsley\":3,\n  \"cayenne pepper\":4,\n  \"lemon juice\":8,\n  \"honey\":36,\n  \"molasses\":14,\n  \"paprika\":2,\n  \"chili powder\":4,\n  \"beer\":2,\n  \"ground cinnamon\":2,\n  \"raisins\":16,\n  \"yeast\":51,\n  \"egg yolks\":2,\n  \"chives\":2,\n  \"baking powder\":44,\n  \"cornstarch\":4,\n  \"dried basil\":3,\n  \"catsup\":2,\n  \"cinnamon\":26,\n  \"cream of tartar\":3,\n  \"tomatoes\":3,\n  \"cumin seeds\":2,\n  \"white pepper\":2,\n  \"eggs\":33,\n  \"margarine\":15,\n  \"butter\":30,\n  \"garlic powder\":6,\n  \"orange juice\":3,\n  \"vanilla\":18,\n  \"whole wheat flour\":70,\n  \"salt\":164,\n  \"black pepper\":8,\n  \"tumeric\":2,\n  \"black olives\":2,\n  \"buttermilk\":9,\n  \"powdered sugar\":2,\n  \"dried oregano\":3,\n  \"cornmeal\":16,\n  \"cloves\":7,\n  \"oil\":20,\n  \"chicken\":2,\n  \"allspice\":6,\n  \"canola oil\":3,\n  \"dijon mustard\":2,\n  \"soy sauce\":2,\n  \"apples\":2,\n  \"maple syrup\":3,\n  \"nutmeg\":11,\n  \"walnuts\":5,\n  \"egg whites\":4,\n  \"baking soda\":48,\n  \"worcestershire sauce\":2,\n  \"tabasco sauce\":2,\n  \"brown sugar\":35,\n  \"ginger\":10,\n  \"lime juice\":3,\n  \"dry mustard\":2,\n  \"fresh rosemary\":2,\n  \"blueberries\":2,\n  \"peanut butter\":3,\n  \"green onions\":4,\n  \"olive oil\":17,\n  \"vegetable oil\":21,\n  \"skim milk\":5,\n  \"flour\":4,\n  \"curry powder\":2,\n  \"shortening\":17,\n  \"applesauce\":9,\n  \"parmesan cheese\":4,\n  \"carrots\":3,\n  \"oats\":5,\n  \"whipping cream\":2,\n  \"sugar\":93},\n \"cauliflower\":\n {\"sour cream\":2,\n  \"yogurt\":2,\n  \"cream cheese\":3,\n  \"tofu\":4,\n  \"cherry tomatoes\":3,\n  \"bacon\":2,\n  \"coconut oil\":3,\n  \"cayenne pepper\":8,\n  \"parsley\":9,\n  \"green olives\":3,\n  \"honey\":2,\n  \"lemon juice\":15,\n  \"zucchini squash\":2,\n  \"paprika\":6,\n  \"chili powder\":3,\n  \"ground cinnamon\":2,\n  \"bread\":2,\n  \"raisins\":2,\n  \"garam masala\":10,\n  \"chives\":3,\n  \"egg yolks\":2,\n  \"green pepper\":10,\n  \"cornstarch\":11,\n  \"baking powder\":2,\n  \"dried basil\":3,\n  \"catsup\":2,\n  \"radishes\":2,\n  \"sesame oil\":3,\n  \"cinnamon\":3,\n  \"pine nuts\":2,\n  \"cheddar cheese\":8,\n  \"sweet red pepper\":4,\n  \"tomatoes\":23,\n  \"cumin seeds\":3,\n  \"white pepper\":5,\n  \"brown rice\":2,\n  \"red pepper flakes\":3,\n  \"eggs\":12,\n  \"margarine\":2,\n  \"fresh ginger\":2,\n  \"frozen peas\":2,\n  \"butter\":15,\n  \"garlic powder\":7,\n  \"dried thyme\":3,\n  \"lemon\":4,\n  \"salt\":89,\n  \"black pepper\":9,\n  \"tumeric\":4,\n  \"fresh basil\":2,\n  \"bean sprouts\":5,\n  \"black olives\":3,\n  \"potatoes\":24,\n  \"eggplant\":5,\n  \"capers\":4,\n  \"cloves\":2,\n  \"frozen spinach\":2,\n  \"oil\":29,\n  \"chicken\":3,\n  \"cucumber\":2,\n  \"canola oil\":3,\n  \"celery seeds\":3,\n  \"dijon mustard\":4,\n  \"soy sauce\":12,\n  \"celery\":20,\n  \"broccoli\":30,\n  \"alfalfa sprouts\":2,\n  \"fresh cilantro\":2,\n  \"nutmeg\":6,\n  \"egg whites\":2,\n  \"bread crumbs\":3,\n  \"worcestershire sauce\":4,\n  \"brown sugar\":3,\n  \"ginger\":11,\n  \"balsamic vinegar\":2,\n  \"dry mustard\":3,\n  \"mayonnaise\":4,\n  \"cumin\":12,\n  \"green onions\":6,\n  \"olive oil\":18,\n  \"vegetable oil\":14,\n  \"skim milk\":2,\n  \"beets\":2,\n  \"curry powder\":4,\n  \"flour\":14,\n  \"kidney beans\":2,\n  \"vinegar\":12,\n  \"white wine\":2,\n  \"parmesan cheese\":10,\n  \"sherry\":4,\n  \"carrots\":52,\n  \"whipping cream\":3,\n  \"chicken breast\":2,\n  \"mushrooms\":16,\n  \"sugar\":26},\n \"flour tortilla\":\n {\"fresh basil\":2,\n  \"sour cream\":7,\n  \"yogurt\":2,\n  \"potatoes\":3,\n  \"buttermilk\":2,\n  \"bacon\":4,\n  \"dried oregano\":4,\n  \"eggplant\":2,\n  \"salsa\":5,\n  \"parsley\":2,\n  \"lemon juice\":2,\n  \"paprika\":2,\n  \"allspice\":2,\n  \"red wine vinegar\":2,\n  \"green pepper\":2,\n  \"fresh cilantro\":3,\n  \"cinnamon\":2,\n  \"lime juice\":2,\n  \"cheddar cheese\":6,\n  \"mozzarella cheese\":3,\n  \"mayonnaise\":2,\n  \"peanut butter\":2,\n  \"green onions\":3,\n  \"vegetable oil\":5,\n  \"white pepper\":2,\n  \"olive oil\":7,\n  \"flour\":2,\n  \"eggs\":7,\n  \"pinto beans\":2,\n  \"margarine\":2,\n  \"butter\":4,\n  \"garlic powder\":2,\n  \"dried thyme\":2,\n  \"chicken breast\":2,\n  \"salt\":7},\n \"applesauce\":\n {\"sour cream\":5,\n  \"yogurt\":6,\n  \"red wine\":3,\n  \"tofu\":2,\n  \"cream cheese\":2,\n  \"bacon\":2,\n  \"parsley\":2,\n  \"cayenne pepper\":3,\n  \"lemon juice\":15,\n  \"honey\":62,\n  \"molasses\":21,\n  \"chili powder\":3,\n  \"beer\":2,\n  \"paprika\":2,\n  \"ground cinnamon\":33,\n  \"raisins\":71,\n  \"yeast\":89,\n  \"egg yolks\":3,\n  \"cornstarch\":3,\n  \"baking powder\":167,\n  \"orange\":2,\n  \"dried basil\":4,\n  \"catsup\":4,\n  \"cream of tartar\":4,\n  \"ground beef\":4,\n  \"cinnamon\":170,\n  \"cheddar cheese\":2,\n  \"almonds\":4,\n  \"eggs\":81,\n  \"margarine\":22,\n  \"fresh ginger\":2,\n  \"butter\":40,\n  \"garlic powder\":3,\n  \"vanilla\":87,\n  \"orange juice\":9,\n  \"whole wheat flour\":56,\n  \"lemon\":2,\n  \"black pepper\":3,\n  \"salt\":280,\n  \"potatoes\":4,\n  \"buttermilk\":14,\n  \"powdered sugar\":12,\n  \"eggplant\":2,\n  \"dried oregano\":3,\n  \"almond extract\":5,\n  \"cornmeal\":14,\n  \"cloves\":42,\n  \"oil\":14,\n  \"blue cheese\":2,\n  \"allspice\":29,\n  \"canola oil\":6,\n  \"strawberries\":4,\n  \"celery\":2,\n  \"apples\":20,\n  \"sweet potatoes\":3,\n  \"maple syrup\":10,\n  \"nutmeg\":66,\n  \"walnuts\":13,\n  \"egg whites\":55,\n  \"bread crumbs\":2,\n  \"baking soda\":145,\n  \"worcestershire sauce\":2,\n  \"corn syrup\":3,\n  \"brown sugar\":91,\n  \"ginger\":16,\n  \"dry mustard\":4,\n  \"white flour\":9,\n  \"pecans\":10,\n  \"blueberries\":5,\n  \"peanut butter\":5,\n  \"cumin\":2,\n  \"vegetable oil\":26,\n  \"banana\":5,\n  \"skim milk\":26,\n  \"curry powder\":4,\n  \"flour\":124,\n  \"vinegar\":7,\n  \"shortening\":18,\n  \"parmesan cheese\":3,\n  \"sherry\":2,\n  \"carrots\":9,\n  \"oats\":23,\n  \"whipping cream\":4,\n  \"sugar\":194},\n \"corn syrup\":\n {\"sour cream\":3,\n  \"cream cheese\":4,\n  \"cayenne pepper\":2,\n  \"parsley\":3,\n  \"honey\":5,\n  \"lemon juice\":9,\n  \"molasses\":5,\n  \"chili powder\":3,\n  \"ground cinnamon\":7,\n  \"raisins\":23,\n  \"red wine vinegar\":2,\n  \"yeast\":4,\n  \"egg yolks\":3,\n  \"baking powder\":26,\n  \"cornstarch\":13,\n  \"orange\":2,\n  \"cream of tartar\":8,\n  \"cinnamon\":31,\n  \"tomatoes\":2,\n  \"almonds\":9,\n  \"eggs\":49,\n  \"margarine\":27,\n  \"butter\":108,\n  \"garlic powder\":2,\n  \"orange juice\":7,\n  \"vanilla\":124,\n  \"whole wheat flour\":5,\n  \"salt\":133,\n  \"buttermilk\":7,\n  \"powdered sugar\":4,\n  \"almond extract\":6,\n  \"cloves\":8,\n  \"oil\":3,\n  \"allspice\":4,\n  \"dijon mustard\":3,\n  \"soy sauce\":5,\n  \"strawberries\":3,\n  \"apples\":7,\n  \"maple syrup\":2,\n  \"sweet potatoes\":4,\n  \"nutmeg\":11,\n  \"walnuts\":11,\n  \"egg whites\":35,\n  \"baking soda\":50,\n  \"worcestershire sauce\":4,\n  \"brown sugar\":60,\n  \"ginger\":7,\n  \"dry mustard\":2,\n  \"pecans\":21,\n  \"blueberries\":4,\n  \"peanut butter\":13,\n  \"cumin\":2,\n  \"chicken wings\":2,\n  \"olive oil\":2,\n  \"vegetable oil\":2,\n  \"banana\":2,\n  \"skim milk\":9,\n  \"curry powder\":2,\n  \"flour\":40,\n  \"vinegar\":8,\n  \"shortening\":13,\n  \"applesauce\":3,\n  \"white wine\":3,\n  \"carrots\":2,\n  \"oats\":3,\n  \"whipping cream\":7,\n  \"sugar\":183},\n \"fresh cilantro\":\n {\"red wine\":2,\n  \"canned beef broth\":2,\n  \"bacon\":5,\n  \"cayenne pepper\":19,\n  \"honey\":14,\n  \"lemon juice\":21,\n  \"paprika\":9,\n  \"ground cinnamon\":7,\n  \"yeast\":2,\n  \"green pepper\":9,\n  \"baking powder\":4,\n  \"saffron\":2,\n  \"sesame oil\":13,\n  \"cinnamon\":5,\n  \"pine nuts\":3,\n  \"avocado\":11,\n  \"cheddar cheese\":10,\n  \"tomatoes\":41,\n  \"white pepper\":8,\n  \"black beans\":23,\n  \"pinto beans\":4,\n  \"fresh garlic\":2,\n  \"cauliflower\":2,\n  \"butter\":13,\n  \"frozen peas\":2,\n  \"orange juice\":7,\n  \"black pepper\":14,\n  \"lemon\":5,\n  \"black olives\":4,\n  \"buttermilk\":3,\n  \"hoisin sauce\":4,\n  \"dried oregano\":15,\n  \"eggplant\":5,\n  \"cornmeal\":3,\n  \"oil\":10,\n  \"chicken\":3,\n  \"cucumber\":9,\n  \"canola oil\":8,\n  \"flour tortilla\":3,\n  \"dried tarragon\":2,\n  \"celery\":9,\n  \"alfalfa sprouts\":2,\n  \"cornish game hens\":2,\n  \"nutmeg\":3,\n  \"baking soda\":2,\n  \"bread crumbs\":2,\n  \"tabasco sauce\":7,\n  \"ginger\":8,\n  \"balsamic vinegar\":3,\n  \"lime\":8,\n  \"peanut butter\":3,\n  \"frozen corn\":3,\n  \"cumin\":24,\n  \"green onions\":43,\n  \"skim milk\":2,\n  \"kidney beans\":3,\n  \"shortening\":2,\n  \"parmesan cheese\":3,\n  \"carrots\":14,\n  \"mushrooms\":3,\n  \"sour cream\":23,\n  \"yogurt\":2,\n  \"cream cheese\":3,\n  \"cherry tomatoes\":2,\n  \"salsa\":14,\n  \"parsley\":9,\n  \"green olives\":2,\n  \"rice vinegar\":15,\n  \"chili powder\":39,\n  \"beer\":4,\n  \"raisins\":2,\n  \"red wine vinegar\":11,\n  \"garam masala\":3,\n  \"cornstarch\":5,\n  \"orange\":4,\n  \"radishes\":3,\n  \"ground beef\":6,\n  \"mozzarella cheese\":3,\n  \"sweet red pepper\":5,\n  \"cumin seeds\":7,\n  \"red pepper flakes\":5,\n  \"eggs\":7,\n  \"margarine\":5,\n  \"fresh ginger\":10,\n  \"garlic powder\":9,\n  \"dried thyme\":4,\n  \"whole wheat flour\":2,\n  \"salt\":144,\n  \"fresh basil\":4,\n  \"bean sprouts\":2,\n  \"potatoes\":7,\n  \"dijon mustard\":2,\n  \"soy sauce\":29,\n  \"maple syrup\":2,\n  \"egg whites\":2,\n  \"worcestershire sauce\":3,\n  \"dried cilantro\":3,\n  \"brown sugar\":4,\n  \"lime juice\":59,\n  \"dry mustard\":2,\n  \"raw shrimp\":2,\n  \"mayonnaise\":8,\n  \"fresh thyme\":3,\n  \"olive oil\":54,\n  \"vegetable oil\":54,\n  \"curry powder\":6,\n  \"flour\":5,\n  \"vinegar\":2,\n  \"white wine\":5,\n  \"whipping cream\":2,\n  \"chicken breast\":3,\n  \"sugar\":25},\n \"beer\":\n {\"red wine\":2,\n  \"bacon\":14,\n  \"cayenne pepper\":21,\n  \"honey\":15,\n  \"lemon juice\":20,\n  \"swiss cheese\":2,\n  \"paprika\":43,\n  \"ground cinnamon\":5,\n  \"bread\":3,\n  \"yeast\":10,\n  \"egg yolks\":9,\n  \"green pepper\":5,\n  \"baking powder\":31,\n  \"saffron\":3,\n  \"sesame oil\":3,\n  \"cream of tartar\":2,\n  \"cinnamon\":19,\n  \"avocado\":2,\n  \"cheddar cheese\":7,\n  \"tomatoes\":20,\n  \"white pepper\":5,\n  \"black beans\":5,\n  \"fresh garlic\":2,\n  \"pinto beans\":5,\n  \"butter\":51,\n  \"salmon\":2,\n  \"orange juice\":2,\n  \"lemon\":10,\n  \"black pepper\":22,\n  \"dried oregano\":10,\n  \"hoisin sauce\":2,\n  \"eggplant\":2,\n  \"capers\":2,\n  \"cornmeal\":4,\n  \"cloves\":8,\n  \"oil\":34,\n  \"chicken\":6,\n  \"canola oil\":3,\n  \"celery\":11,\n  \"nutmeg\":9,\n  \"walnuts\":4,\n  \"fresh cilantro\":4,\n  \"bread crumbs\":2,\n  \"baking soda\":10,\n  \"tabasco sauce\":22,\n  \"ginger\":6,\n  \"lime\":5,\n  \"pecans\":3,\n  \"peanut butter\":3,\n  \"green onions\":4,\n  \"cumin\":41,\n  \"frozen corn\":2,\n  \"kidney beans\":3,\n  \"shortening\":5,\n  \"parmesan cheese\":5,\n  \"applesauce\":2,\n  \"carrots\":8,\n  \"mushrooms\":12,\n  \"sour cream\":11,\n  \"yogurt\":2,\n  \"brussels sprouts\":3,\n  \"cream cheese\":3,\n  \"salsa\":2,\n  \"parsley\":16,\n  \"molasses\":14,\n  \"chili powder\":45,\n  \"raisins\":4,\n  \"red wine vinegar\":13,\n  \"cornstarch\":11,\n  \"dried basil\":2,\n  \"catsup\":11,\n  \"ground beef\":9,\n  \"sweet red pepper\":2,\n  \"cumin seeds\":3,\n  \"red pepper flakes\":5,\n  \"eggs\":24,\n  \"margarine\":6,\n  \"garlic powder\":16,\n  \"dried thyme\":7,\n  \"cream of mushroom soup\":2,\n  \"vanilla\":7,\n  \"whole wheat flour\":5,\n  \"salt\":166,\n  \"fresh basil\":2,\n  \"potatoes\":11,\n  \"allspice\":9,\n  \"dijon mustard\":12,\n  \"soy sauce\":23,\n  \"broccoli\":3,\n  \"apples\":4,\n  \"yellow mustard\":2,\n  \"maple syrup\":3,\n  \"egg whites\":3,\n  \"worcestershire sauce\":38,\n  \"brown sugar\":43,\n  \"lime juice\":6,\n  \"dry mustard\":31,\n  \"white flour\":2,\n  \"mayonnaise\":8,\n  \"chicken wings\":5,\n  \"olive oil\":33,\n  \"vegetable oil\":31,\n  \"flour\":88,\n  \"curry powder\":2,\n  \"vinegar\":11,\n  \"chicken breast\":2,\n  \"sugar\":68},\n \"white rice\":\n {\"red wine\":2,\n  \"bacon\":2,\n  \"salsa\":2,\n  \"parsley\":6,\n  \"cayenne pepper\":6,\n  \"rice vinegar\":2,\n  \"lemon juice\":7,\n  \"honey\":2,\n  \"molasses\":2,\n  \"paprika\":4,\n  \"chili powder\":5,\n  \"ground cinnamon\":4,\n  \"raisins\":5,\n  \"egg yolks\":2,\n  \"chives\":2,\n  \"green pepper\":7,\n  \"cornstarch\":3,\n  \"dried basil\":2,\n  \"saffron\":2,\n  \"sesame oil\":2,\n  \"ground beef\":2,\n  \"cinnamon\":5,\n  \"pine nuts\":2,\n  \"avocado\":2,\n  \"tomatoes\":7,\n  \"sweet red pepper\":2,\n  \"cumin seeds\":3,\n  \"white pepper\":4,\n  \"brown rice\":3,\n  \"red pepper flakes\":3,\n  \"black beans\":5,\n  \"eggs\":8,\n  \"almonds\":2,\n  \"margarine\":2,\n  \"butter\":16,\n  \"garlic powder\":4,\n  \"dried thyme\":5,\n  \"cream of mushroom soup\":2,\n  \"vanilla\":2,\n  \"black pepper\":11,\n  \"salt\":34,\n  \"lemon\":3,\n  \"fresh basil\":2,\n  \"bean sprouts\":2,\n  \"potatoes\":2,\n  \"dried oregano\":6,\n  \"capers\":2,\n  \"oil\":5,\n  \"chicken\":3,\n  \"soy sauce\":4,\n  \"celery\":4,\n  \"nutmeg\":2,\n  \"bread crumbs\":2,\n  \"worcestershire sauce\":3,\n  \"tabasco sauce\":3,\n  \"lime juice\":3,\n  \"green onions\":5,\n  \"cumin\":5,\n  \"olive oil\":18,\n  \"vegetable oil\":4,\n  \"skim milk\":4,\n  \"flour\":3,\n  \"curry powder\":5,\n  \"vinegar\":2,\n  \"parmesan cheese\":3,\n  \"white wine\":3,\n  \"sherry\":2,\n  \"chicken breast\":2,\n  \"mushrooms\":5,\n  \"sugar\":8},\n \"carrots\":\n {\"elbow macaroni\":7,\n  \"red wine\":38,\n  \"coconut oil\":2,\n  \"bacon\":43,\n  \"cayenne pepper\":59,\n  \"lemon juice\":124,\n  \"honey\":56,\n  \"zucchini squash\":2,\n  \"swiss cheese\":5,\n  \"paprika\":81,\n  \"bread\":6,\n  \"ground cinnamon\":41,\n  \"yeast\":5,\n  \"egg yolks\":9,\n  \"green pepper\":114,\n  \"baking powder\":70,\n  \"saffron\":6,\n  \"sesame oil\":49,\n  \"pine nuts\":10,\n  \"cinnamon\":97,\n  \"avocado\":2,\n  \"cheddar cheese\":17,\n  \"tomatoes\":195,\n  \"white pepper\":39,\n  \"brown rice\":24,\n  \"black beans\":16,\n  \"fresh garlic\":3,\n  \"pinto beans\":10,\n  \"cauliflower\":52,\n  \"butter\":280,\n  \"frozen peas\":45,\n  \"salmon\":2,\n  \"orange juice\":54,\n  \"black pepper\":150,\n  \"lemon\":34,\n  \"tumeric\":5,\n  \"black olives\":7,\n  \"buttermilk\":10,\n  \"powdered sugar\":14,\n  \"hoisin sauce\":8,\n  \"eggplant\":18,\n  \"dried oregano\":43,\n  \"capers\":8,\n  \"cornmeal\":4,\n  \"cloves\":44,\n  \"oil\":208,\n  \"chicken\":59,\n  \"blue cheese\":3,\n  \"cucumber\":27,\n  \"dry rosemary\":2,\n  \"canola oil\":25,\n  \"dried tarragon\":5,\n  \"strawberries\":2,\n  \"celery\":279,\n  \"alfalfa sprouts\":2,\n  \"cornish game hens\":2,\n  \"fresh cilantro\":14,\n  \"nutmeg\":55,\n  \"walnuts\":25,\n  \"baking soda\":55,\n  \"bread crumbs\":18,\n  \"corn syrup\":2,\n  \"tabasco sauce\":17,\n  \"balsamic vinegar\":17,\n  \"ginger\":52,\n  \"fresh rosemary\":7,\n  \"lime\":4,\n  \"pecans\":12,\n  \"peanut butter\":14,\n  \"frozen corn\":12,\n  \"cumin\":65,\n  \"green onions\":77,\n  \"canned chicken broth\":2,\n  \"beets\":24,\n  \"banana\":4,\n  \"skim milk\":18,\n  \"barley\":36,\n  \"kidney beans\":21,\n  \"shortening\":13,\n  \"applesauce\":9,\n  \"parmesan cheese\":40,\n  \"mushrooms\":139,\n  \"sour cream\":43,\n  \"yogurt\":9,\n  \"brussels sprouts\":3,\n  \"cream cheese\":17,\n  \"tofu\":12,\n  \"cherry tomatoes\":12,\n  \"salsa\":3,\n  \"parsley\":189,\n  \"green olives\":4,\n  \"rice vinegar\":16,\n  \"molasses\":8,\n  \"beer\":8,\n  \"chili powder\":49,\n  \"spaghetti\":7,\n  \"raisins\":61,\n  \"red wine vinegar\":32,\n  \"garam masala\":5,\n  \"chives\":16,\n  \"cornstarch\":122,\n  \"orange\":18,\n  \"dried basil\":55,\n  \"radishes\":19,\n  \"catsup\":23,\n  \"ground beef\":31,\n  \"mozzarella cheese\":9,\n  \"sweet red pepper\":15,\n  \"sweet red onions\":2,\n  \"cumin seeds\":10,\n  \"barbecue sauce\":4,\n  \"raw spinach\":2,\n  \"red pepper flakes\":20,\n  \"almonds\":12,\n  \"eggs\":119,\n  \"margarine\":71,\n  \"fresh ginger\":20,\n  \"kale\":3,\n  \"garlic powder\":58,\n  \"dried thyme\":85,\n  \"cream of mushroom soup\":13,\n  \"vanilla\":34,\n  \"whole wheat flour\":21,\n  \"salt\":1057,\n  \"fresh basil\":17,\n  \"bean sprouts\":18,\n  \"shiitake mushrooms\":8,\n  \"potatoes\":369,\n  \"almond extract\":2,\n  \"frozen spinach\":3,\n  \"celery seeds\":8,\n  \"allspice\":16,\n  \"dijon mustard\":34,\n  \"soy sauce\":146,\n  \"broccoli\":48,\n  \"chicken legs\":6,\n  \"apples\":24,\n  \"yellow mustard\":2,\n  \"shell macaroni\":3,\n  \"maple syrup\":9,\n  \"sweet potatoes\":22,\n  \"egg whites\":17,\n  \"worcestershire sauce\":85,\n  \"dried cilantro\":2,\n  \"brown sugar\":69,\n  \"lime juice\":17,\n  \"dry mustard\":36,\n  \"raw shrimp\":2,\n  \"white flour\":3,\n  \"mayonnaise\":32,\n  \"chicken wings\":3,\n  \"fresh thyme\":14,\n  \"olive oil\":352,\n  \"vegetable oil\":184,\n  \"flour\":282,\n  \"curry powder\":59,\n  \"vinegar\":84,\n  \"white wine\":39,\n  \"sherry\":35,\n  \"yam\":2,\n  \"oats\":4,\n  \"whipping cream\":12,\n  \"chicken breast\":10,\n  \"sugar\":316},\n \"almonds\":\n {\"red wine\":4,\n  \"bacon\":4,\n  \"cayenne pepper\":4,\n  \"honey\":45,\n  \"lemon juice\":40,\n  \"swiss cheese\":2,\n  \"paprika\":19,\n  \"ground cinnamon\":26,\n  \"bread\":3,\n  \"yeast\":20,\n  \"egg yolks\":28,\n  \"green pepper\":9,\n  \"baking powder\":93,\n  \"saffron\":11,\n  \"sesame oil\":4,\n  \"cinnamon\":75,\n  \"pine nuts\":12,\n  \"cream of tartar\":7,\n  \"avocado\":2,\n  \"cheddar cheese\":3,\n  \"corn tortilla\":2,\n  \"tomatoes\":18,\n  \"white pepper\":4,\n  \"brown rice\":3,\n  \"black beans\":2,\n  \"pinto beans\":4,\n  \"butter\":187,\n  \"salmon\":2,\n  \"orange juice\":10,\n  \"lemon\":18,\n  \"black pepper\":13,\n  \"buttermilk\":5,\n  \"powdered sugar\":49,\n  \"dried oregano\":3,\n  \"eggplant\":2,\n  \"capers\":2,\n  \"cornmeal\":2,\n  \"cloves\":33,\n  \"oil\":37,\n  \"chicken\":27,\n  \"blue cheese\":2,\n  \"dry rosemary\":2,\n  \"canola oil\":4,\n  \"strawberries\":4,\n  \"celery\":22,\n  \"nutmeg\":30,\n  \"walnuts\":18,\n  \"baking soda\":40,\n  \"bread crumbs\":6,\n  \"corn syrup\":9,\n  \"ginger\":18,\n  \"fresh rosemary\":2,\n  \"pecans\":7,\n  \"peanut butter\":3,\n  \"cumin\":13,\n  \"green onions\":9,\n  \"skim milk\":4,\n  \"banana\":6,\n  \"barley\":2,\n  \"shortening\":10,\n  \"applesauce\":4,\n  \"parmesan cheese\":5,\n  \"carrots\":12,\n  \"mushrooms\":10,\n  \"sour cream\":22,\n  \"yogurt\":12,\n  \"tofu\":2,\n  \"cream cheese\":31,\n  \"cherry tomatoes\":2,\n  \"parsley\":19,\n  \"green olives\":3,\n  \"molasses\":8,\n  \"chili powder\":5,\n  \"spaghetti\":2,\n  \"raisins\":80,\n  \"red wine vinegar\":2,\n  \"garam masala\":5,\n  \"chives\":5,\n  \"cornstarch\":28,\n  \"orange\":6,\n  \"ground beef\":4,\n  \"cumin seeds\":4,\n  \"eggs\":132,\n  \"margarine\":32,\n  \"fresh ginger\":4,\n  \"garlic powder\":7,\n  \"dried thyme\":3,\n  \"vanilla\":82,\n  \"whole wheat flour\":6,\n  \"salt\":240,\n  \"potatoes\":5,\n  \"almond extract\":74,\n  \"allspice\":22,\n  \"dijon mustard\":3,\n  \"soy sauce\":9,\n  \"apples\":14,\n  \"maple syrup\":6,\n  \"sweet potatoes\":3,\n  \"white rice\":2,\n  \"egg whites\":36,\n  \"worcestershire sauce\":4,\n  \"brown sugar\":44,\n  \"lime juice\":4,\n  \"dry mustard\":2,\n  \"blueberries\":2,\n  \"mayonnaise\":11,\n  \"chicken wings\":2,\n  \"vegetable oil\":29,\n  \"olive oil\":19,\n  \"flour\":162,\n  \"curry powder\":10,\n  \"vinegar\":13,\n  \"white wine\":6,\n  \"sherry\":8,\n  \"oats\":8,\n  \"whipping cream\":25,\n  \"chicken breast\":3,\n  \"sugar\":323},\n \"chicken legs\":\n {\"fresh basil\":2,\n  \"red wine\":2,\n  \"dried oregano\":3,\n  \"cayenne pepper\":2,\n  \"honey\":3,\n  \"lemon juice\":6,\n  \"oil\":5,\n  \"chili powder\":2,\n  \"paprika\":3,\n  \"soy sauce\":8,\n  \"sweet potatoes\":2,\n  \"green pepper\":2,\n  \"nutmeg\":3,\n  \"egg whites\":2,\n  \"bread crumbs\":2,\n  \"cornstarch\":7,\n  \"worcestershire sauce\":2,\n  \"dried basil\":4,\n  \"sesame oil\":3,\n  \"catsup\":2,\n  \"brown sugar\":2,\n  \"cinnamon\":2,\n  \"ginger\":3,\n  \"tomatoes\":4,\n  \"green onions\":2,\n  \"canned chicken broth\":2,\n  \"vegetable oil\":7,\n  \"olive oil\":9,\n  \"barley\":2,\n  \"curry powder\":2,\n  \"flour\":3,\n  \"margarine\":2,\n  \"shortening\":2,\n  \"frozen peas\":3,\n  \"butter\":5,\n  \"garlic powder\":2,\n  \"dried thyme\":5,\n  \"sherry\":2,\n  \"carrots\":6,\n  \"whipping cream\":2,\n  \"orange juice\":2,\n  \"salt\":20,\n  \"mushrooms\":4,\n  \"sugar\":6,\n  \"lemon\":2,\n  \"black pepper\":3},\n \"parmesan cheese\":\n {\"elbow macaroni\":11,\n  \"red wine\":15,\n  \"canned beef broth\":2,\n  \"bacon\":42,\n  \"cayenne pepper\":26,\n  \"lemon juice\":77,\n  \"honey\":7,\n  \"zucchini squash\":3,\n  \"swiss cheese\":32,\n  \"paprika\":79,\n  \"camembert cheese\":2,\n  \"bread\":15,\n  \"yeast\":30,\n  \"egg yolks\":25,\n  \"green pepper\":43,\n  \"baking powder\":12,\n  \"saffron\":6,\n  \"cinnamon\":6,\n  \"cream of tartar\":3,\n  \"pine nuts\":43,\n  \"avocado\":5,\n  \"cheddar cheese\":36,\n  \"tomatoes\":85,\n  \"white pepper\":33,\n  \"brown rice\":4,\n  \"black beans\":3,\n  \"fresh garlic\":4,\n  \"cauliflower\":10,\n  \"frozen peas\":15,\n  \"butter\":376,\n  \"salmon\":2,\n  \"black pepper\":102,\n  \"lemon\":27,\n  \"black olives\":19,\n  \"buttermilk\":8,\n  \"eggplant\":37,\n  \"dried oregano\":66,\n  \"capers\":15,\n  \"cornmeal\":13,\n  \"cloves\":3,\n  \"oil\":62,\n  \"chicken\":28,\n  \"blue cheese\":3,\n  \"canola oil\":5,\n  \"dried tarragon\":3,\n  \"celery\":34,\n  \"fresh cilantro\":3,\n  \"nutmeg\":78,\n  \"walnuts\":13,\n  \"baking soda\":5,\n  \"bread crumbs\":84,\n  \"tabasco sauce\":16,\n  \"ginger\":4,\n  \"balsamic vinegar\":16,\n  \"fresh rosemary\":8,\n  \"pecans\":2,\n  \"cumin\":5,\n  \"green onions\":55,\n  \"banana\":2,\n  \"skim milk\":22,\n  \"beets\":3,\n  \"kidney beans\":4,\n  \"shortening\":6,\n  \"applesauce\":3,\n  \"carrots\":40,\n  \"mushrooms\":112,\n  \"sour cream\":64,\n  \"yogurt\":5,\n  \"cream cheese\":36,\n  \"tofu\":3,\n  \"cherry tomatoes\":9,\n  \"salsa\":3,\n  \"parsley\":182,\n  \"green olives\":4,\n  \"chili powder\":10,\n  \"beer\":5,\n  \"spaghetti\":34,\n  \"raisins\":5,\n  \"red wine vinegar\":17,\n  \"chives\":15,\n  \"cornstarch\":20,\n  \"orange\":2,\n  \"dried basil\":77,\n  \"catsup\":4,\n  \"ground beef\":72,\n  \"adzuki beans\":2,\n  \"mozzarella cheese\":170,\n  \"romaine lettuce\":8,\n  \"sweet red pepper\":7,\n  \"cumin seeds\":2,\n  \"raw spinach\":2,\n  \"red pepper flakes\":16,\n  \"eggs\":209,\n  \"almonds\":5,\n  \"margarine\":64,\n  \"fresh ginger\":3,\n  \"kale\":5,\n  \"garlic powder\":107,\n  \"dried thyme\":22,\n  \"cream of mushroom soup\":17,\n  \"low fat milk\":3,\n  \"vanilla\":2,\n  \"whole wheat flour\":7,\n  \"salt\":640,\n  \"fresh basil\":53,\n  \"shiitake mushrooms\":6,\n  \"potatoes\":29,\n  \"frozen spinach\":13,\n  \"allspice\":2,\n  \"dijon mustard\":24,\n  \"soy sauce\":7,\n  \"broccoli\":20,\n  \"apples\":2,\n  \"maple syrup\":2,\n  \"white rice\":3,\n  \"egg whites\":18,\n  \"worcestershire sauce\":56,\n  \"brown sugar\":10,\n  \"lime juice\":5,\n  \"dry mustard\":28,\n  \"white flour\":4,\n  \"mayonnaise\":75,\n  \"chicken wings\":3,\n  \"vegetable oil\":45,\n  \"olive oil\":477,\n  \"fresh thyme\":9,\n  \"curry powder\":7,\n  \"flour\":204,\n  \"vinegar\":6,\n  \"white wine\":43,\n  \"sherry\":26,\n  \"oats\":2,\n  \"whipping cream\":30,\n  \"chicken breast\":12,\n  \"sugar\":99},\n \"dried basil\":\n {\"elbow macaroni\":6,\n  \"red wine\":12,\n  \"canned tomato sauce\":2,\n  \"bacon\":10,\n  \"cayenne pepper\":24,\n  \"lemon juice\":54,\n  \"honey\":12,\n  \"zucchini squash\":2,\n  \"swiss cheese\":4,\n  \"paprika\":31,\n  \"ground cinnamon\":3,\n  \"dried bay leaf\":2,\n  \"yeast\":10,\n  \"egg yolks\":3,\n  \"green pepper\":37,\n  \"baking powder\":10,\n  \"saffron\":2,\n  \"sesame oil\":2,\n  \"cinnamon\":8,\n  \"pine nuts\":10,\n  \"avocado\":2,\n  \"cheddar cheese\":6,\n  \"tomatoes\":77,\n  \"white pepper\":15,\n  \"brown rice\":11,\n  \"black beans\":6,\n  \"fresh garlic\":4,\n  \"pinto beans\":2,\n  \"cauliflower\":3,\n  \"frozen peas\":7,\n  \"butter\":62,\n  \"orange juice\":7,\n  \"lemon\":9,\n  \"black pepper\":69,\n  \"black olives\":11,\n  \"buttermilk\":4,\n  \"dried oregano\":253,\n  \"eggplant\":16,\n  \"capers\":14,\n  \"cornmeal\":9,\n  \"cloves\":5,\n  \"oil\":23,\n  \"chicken\":11,\n  \"cucumber\":5,\n  \"canola oil\":4,\n  \"dried tarragon\":11,\n  \"celery\":23,\n  \"alfalfa sprouts\":2,\n  \"nutmeg\":6,\n  \"walnuts\":5,\n  \"bread crumbs\":13,\n  \"baking soda\":6,\n  \"tabasco sauce\":9,\n  \"balsamic vinegar\":22,\n  \"fresh rosemary\":3,\n  \"pecans\":3,\n  \"green onions\":27,\n  \"frozen corn\":4,\n  \"cumin\":6,\n  \"canned chicken broth\":5,\n  \"beets\":3,\n  \"skim milk\":11,\n  \"barley\":7,\n  \"kidney beans\":8,\n  \"shortening\":2,\n  \"applesauce\":4,\n  \"parmesan cheese\":77,\n  \"carrots\":55,\n  \"mushrooms\":37,\n  \"sour cream\":9,\n  \"tofu\":2,\n  \"cream cheese\":6,\n  \"cherry tomatoes\":10,\n  \"salsa\":4,\n  \"parsley\":34,\n  \"rice vinegar\":3,\n  \"molasses\":2,\n  \"beer\":2,\n  \"chili powder\":22,\n  \"spaghetti\":8,\n  \"raisins\":3,\n  \"red wine vinegar\":32,\n  \"chives\":2,\n  \"cornstarch\":22,\n  \"catsup\":4,\n  \"ground beef\":29,\n  \"mozzarella cheese\":36,\n  \"romaine lettuce\":2,\n  \"sweet red pepper\":9,\n  \"barbecue sauce\":2,\n  \"red pepper flakes\":20,\n  \"eggs\":50,\n  \"margarine\":23,\n  \"fresh ginger\":2,\n  \"garlic powder\":94,\n  \"dried thyme\":126,\n  \"cream of mushroom soup\":3,\n  \"whole wheat flour\":9,\n  \"salt\":353,\n  \"fresh basil\":28,\n  \"bean sprouts\":2,\n  \"shiitake mushrooms\":4,\n  \"potatoes\":20,\n  \"frozen spinach\":2,\n  \"allspice\":4,\n  \"dijon mustard\":24,\n  \"soy sauce\":15,\n  \"broccoli\":5,\n  \"chicken legs\":4,\n  \"white rice\":2,\n  \"yellow mustard\":2,\n  \"egg whites\":11,\n  \"worcestershire sauce\":28,\n  \"brown sugar\":9,\n  \"lime juice\":3,\n  \"dry mustard\":17,\n  \"raw shrimp\":3,\n  \"white flour\":3,\n  \"mayonnaise\":13,\n  \"vegetable oil\":46,\n  \"olive oil\":239,\n  \"curry powder\":3,\n  \"flour\":35,\n  \"vinegar\":7,\n  \"white wine\":8,\n  \"sherry\":7,\n  \"yam\":2,\n  \"whipping cream\":7,\n  \"chicken breast\":3,\n  \"sugar\":80},\n \"cinnamon\":\n {\"elbow macaroni\":2,\n  \"red wine\":13,\n  \"canned tomato sauce\":2,\n  \"bacon\":8,\n  \"brown onions\":2,\n  \"cayenne pepper\":58,\n  \"honey\":336,\n  \"lemon juice\":299,\n  \"zucchini squash\":2,\n  \"swiss cheese\":2,\n  \"paprika\":64,\n  \"bread\":18,\n  \"ground cinnamon\":10,\n  \"yeast\":121,\n  \"egg yolks\":75,\n  \"green pepper\":24,\n  \"baking powder\":1027,\n  \"saffron\":13,\n  \"sesame oil\":2,\n  \"pine nuts\":21,\n  \"cream of tartar\":40,\n  \"avocado\":3,\n  \"cheddar cheese\":12,\n  \"tomatoes\":70,\n  \"white pepper\":14,\n  \"brown rice\":15,\n  \"black beans\":7,\n  \"pinto beans\":5,\n  \"cauliflower\":3,\n  \"frozen peas\":2,\n  \"butter\":1064,\n  \"salmon\":2,\n  \"orange juice\":178,\n  \"black pepper\":87,\n  \"lemon\":59,\n  \"tumeric\":18,\n  \"black olives\":3,\n  \"buttermilk\":121,\n  \"powdered sugar\":126,\n  \"eggplant\":12,\n  \"dried oregano\":11,\n  \"capers\":10,\n  \"cornmeal\":22,\n  \"cloves\":583,\n  \"oil\":316,\n  \"chicken\":27,\n  \"cucumber\":3,\n  \"flour tortilla\":2,\n  \"canola oil\":29,\n  \"dried tarragon\":2,\n  \"strawberries\":22,\n  \"celery\":22,\n  \"cornish game hens\":3,\n  \"fresh cilantro\":5,\n  \"fresh peaches\":13,\n  \"nutmeg\":1126,\n  \"walnuts\":199,\n  \"bread crumbs\":27,\n  \"baking soda\":991,\n  \"tabasco sauce\":7,\n  \"corn syrup\":31,\n  \"balsamic vinegar\":4,\n  \"ginger\":394,\n  \"lime\":12,\n  \"pecans\":131,\n  \"peanut butter\":20,\n  \"frozen corn\":3,\n  \"cumin\":73,\n  \"green onions\":11,\n  \"beets\":7,\n  \"banana\":29,\n  \"skim milk\":94,\n  \"barley\":2,\n  \"kidney beans\":3,\n  \"shortening\":300,\n  \"parmesan cheese\":6,\n  \"applesauce\":170,\n  \"carrots\":97,\n  \"mushrooms\":9,\n  \"sour cream\":123,\n  \"yogurt\":18,\n  \"cream cheese\":105,\n  \"tofu\":4,\n  \"cherry tomatoes\":2,\n  \"salsa\":3,\n  \"parsley\":36,\n  \"green olives\":6,\n  \"molasses\":221,\n  \"beer\":19,\n  \"chili powder\":62,\n  \"spaghetti\":6,\n  \"raisins\":746,\n  \"red wine vinegar\":17,\n  \"garam masala\":3,\n  \"chives\":3,\n  \"cornstarch\":151,\n  \"dried basil\":8,\n  \"orange\":44,\n  \"radishes\":2,\n  \"catsup\":11,\n  \"ground beef\":35,\n  \"sweet red pepper\":5,\n  \"cumin seeds\":15,\n  \"canned peaches\":3,\n  \"barbecue sauce\":2,\n  \"red pepper flakes\":4,\n  \"eggs\":1239,\n  \"almonds\":75,\n  \"margarine\":263,\n  \"fresh ginger\":8,\n  \"garlic powder\":26,\n  \"dried thyme\":10,\n  \"low fat milk\":4,\n  \"vanilla\":842,\n  \"whole wheat flour\":139,\n  \"salt\":2385,\n  \"fresh basil\":2,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":27,\n  \"almond extract\":47,\n  \"celery seeds\":3,\n  \"allspice\":411,\n  \"dijon mustard\":7,\n  \"soy sauce\":27,\n  \"broccoli\":2,\n  \"chicken legs\":2,\n  \"apples\":331,\n  \"maple syrup\":64,\n  \"white rice\":5,\n  \"sweet potatoes\":53,\n  \"egg whites\":167,\n  \"worcestershire sauce\":19,\n  \"brown sugar\":891,\n  \"lime juice\":21,\n  \"dry mustard\":22,\n  \"white flour\":26,\n  \"blueberries\":43,\n  \"mayonnaise\":10,\n  \"chicken wings\":2,\n  \"fresh thyme\":3,\n  \"olive oil\":119,\n  \"vegetable oil\":232,\n  \"flour\":1504,\n  \"curry powder\":38,\n  \"vinegar\":116,\n  \"white wine\":11,\n  \"sherry\":17,\n  \"oats\":64,\n  \"whipping cream\":47,\n  \"chicken breast\":8,\n  \"sugar\":2551},\n \"canned vegetable broth\":\n {\"olive oil\":4,\n  \"chili powder\":2,\n  \"shiitake mushrooms\":2,\n  \"dried oregano\":2,\n  \"green onions\":3,\n  \"salt\":2},\n \"oats\":\n {\"yogurt\":4,\n  \"cream cheese\":2,\n  \"tofu\":2,\n  \"canned tomato sauce\":2,\n  \"cayenne pepper\":2,\n  \"parsley\":7,\n  \"honey\":37,\n  \"lemon juice\":8,\n  \"molasses\":12,\n  \"chili powder\":2,\n  \"ground cinnamon\":7,\n  \"raisins\":39,\n  \"yeast\":43,\n  \"chives\":2,\n  \"cornstarch\":5,\n  \"baking powder\":43,\n  \"orange\":3,\n  \"catsup\":2,\n  \"ground beef\":4,\n  \"cinnamon\":64,\n  \"cream of tartar\":3,\n  \"cheddar cheese\":2,\n  \"tomatoes\":2,\n  \"eggs\":57,\n  \"almonds\":8,\n  \"margarine\":30,\n  \"butter\":53,\n  \"garlic powder\":5,\n  \"vanilla\":66,\n  \"orange juice\":3,\n  \"whole wheat flour\":18,\n  \"salt\":163,\n  \"buttermilk\":5,\n  \"potatoes\":4,\n  \"powdered sugar\":12,\n  \"eggplant\":3,\n  \"almond extract\":8,\n  \"cornmeal\":5,\n  \"cloves\":8,\n  \"oil\":23,\n  \"allspice\":6,\n  \"canola oil\":4,\n  \"soy sauce\":3,\n  \"strawberries\":2,\n  \"celery\":2,\n  \"apples\":10,\n  \"maple syrup\":4,\n  \"nutmeg\":18,\n  \"walnuts\":7,\n  \"egg whites\":13,\n  \"bread crumbs\":4,\n  \"baking soda\":84,\n  \"corn syrup\":3,\n  \"brown sugar\":81,\n  \"ginger\":6,\n  \"dry mustard\":2,\n  \"white flour\":5,\n  \"pecans\":8,\n  \"blueberries\":4,\n  \"peanut butter\":14,\n  \"cumin\":4,\n  \"vegetable oil\":15,\n  \"olive oil\":4,\n  \"banana\":3,\n  \"skim milk\":6,\n  \"beets\":2,\n  \"curry powder\":3,\n  \"flour\":105,\n  \"barley\":2,\n  \"shortening\":24,\n  \"parmesan cheese\":2,\n  \"applesauce\":23,\n  \"carrots\":4,\n  \"sugar\":90},\n \"curry powder\":\n {\"elbow macaroni\":2,\n  \"red wine\":3,\n  \"bacon\":6,\n  \"cayenne pepper\":56,\n  \"lemon juice\":138,\n  \"honey\":55,\n  \"zucchini squash\":2,\n  \"paprika\":77,\n  \"bread\":5,\n  \"ground cinnamon\":20,\n  \"yeast\":4,\n  \"egg yolks\":7,\n  \"green pepper\":43,\n  \"baking powder\":12,\n  \"saffron\":2,\n  \"sesame oil\":11,\n  \"cinnamon\":38,\n  \"pine nuts\":3,\n  \"avocado\":2,\n  \"cheddar cheese\":17,\n  \"tomatoes\":49,\n  \"white pepper\":29,\n  \"brown rice\":13,\n  \"black beans\":4,\n  \"pinto beans\":2,\n  \"fresh garlic\":2,\n  \"cauliflower\":4,\n  \"frozen peas\":12,\n  \"butter\":150,\n  \"orange juice\":28,\n  \"lemon\":16,\n  \"black pepper\":64,\n  \"tumeric\":7,\n  \"black olives\":4,\n  \"buttermilk\":5,\n  \"hoisin sauce\":2,\n  \"eggplant\":8,\n  \"dried oregano\":2,\n  \"capers\":3,\n  \"cloves\":15,\n  \"oil\":79,\n  \"chicken\":36,\n  \"blue cheese\":2,\n  \"cucumber\":10,\n  \"canola oil\":17,\n  \"dried tarragon\":3,\n  \"celery\":53,\n  \"walnuts\":6,\n  \"fresh cilantro\":6,\n  \"nutmeg\":13,\n  \"bread crumbs\":22,\n  \"baking soda\":2,\n  \"corn syrup\":2,\n  \"tabasco sauce\":11,\n  \"balsamic vinegar\":5,\n  \"ginger\":41,\n  \"fresh rosemary\":3,\n  \"lime\":2,\n  \"pecans\":2,\n  \"peanut butter\":14,\n  \"cumin\":50,\n  \"green onions\":43,\n  \"frozen corn\":3,\n  \"canned chicken broth\":2,\n  \"banana\":12,\n  \"skim milk\":12,\n  \"barley\":4,\n  \"kidney beans\":4,\n  \"shortening\":3,\n  \"applesauce\":4,\n  \"parmesan cheese\":7,\n  \"carrots\":59,\n  \"mushrooms\":24,\n  \"sour cream\":54,\n  \"yogurt\":18,\n  \"tofu\":6,\n  \"cream cheese\":16,\n  \"cherry tomatoes\":5,\n  \"parsley\":39,\n  \"green olives\":5,\n  \"rice vinegar\":4,\n  \"molasses\":9,\n  \"beer\":2,\n  \"chili powder\":46,\n  \"raisins\":67,\n  \"red wine vinegar\":6,\n  \"garam masala\":7,\n  \"chives\":9,\n  \"cornstarch\":42,\n  \"dried basil\":3,\n  \"orange\":6,\n  \"radishes\":2,\n  \"catsup\":7,\n  \"ground beef\":11,\n  \"sweet red pepper\":7,\n  \"cumin seeds\":10,\n  \"canned peaches\":2,\n  \"red pepper flakes\":10,\n  \"almonds\":10,\n  \"eggs\":33,\n  \"margarine\":29,\n  \"fresh ginger\":12,\n  \"kale\":2,\n  \"garlic powder\":42,\n  \"dried thyme\":19,\n  \"cream of mushroom soup\":9,\n  \"low fat milk\":2,\n  \"whole wheat flour\":4,\n  \"salt\":474,\n  \"fresh basil\":2,\n  \"bean sprouts\":4,\n  \"shiitake mushrooms\":3,\n  \"potatoes\":28,\n  \"frozen spinach\":3,\n  \"allspice\":13,\n  \"celery seeds\":5,\n  \"dijon mustard\":21,\n  \"soy sauce\":73,\n  \"broccoli\":9,\n  \"chicken legs\":2,\n  \"apples\":29,\n  \"maple syrup\":2,\n  \"sweet potatoes\":14,\n  \"white rice\":5,\n  \"egg whites\":5,\n  \"worcestershire sauce\":32,\n  \"brown sugar\":33,\n  \"lime juice\":22,\n  \"dry mustard\":31,\n  \"raw shrimp\":2,\n  \"white flour\":2,\n  \"mayonnaise\":98,\n  \"chicken wings\":4,\n  \"olive oil\":92,\n  \"vegetable oil\":88,\n  \"fresh thyme\":3,\n  \"flour\":116,\n  \"vinegar\":28,\n  \"white wine\":11,\n  \"sherry\":16,\n  \"oats\":3,\n  \"whipping cream\":11,\n  \"chicken breast\":11,\n  \"sugar\":83},\n \"swiss cheese\":\n {\"sour cream\":14,\n  \"yogurt\":2,\n  \"elbow macaroni\":3,\n  \"cream cheese\":7,\n  \"bacon\":26,\n  \"salsa\":2,\n  \"parsley\":22,\n  \"cayenne pepper\":12,\n  \"green olives\":2,\n  \"honey\":2,\n  \"lemon juice\":7,\n  \"beer\":2,\n  \"paprika\":14,\n  \"spaghetti\":3,\n  \"bread\":4,\n  \"red wine vinegar\":2,\n  \"yeast\":7,\n  \"egg yolks\":7,\n  \"chives\":7,\n  \"green pepper\":11,\n  \"baking powder\":4,\n  \"cornstarch\":4,\n  \"orange\":2,\n  \"dried basil\":4,\n  \"radishes\":2,\n  \"cream of tartar\":3,\n  \"ground beef\":7,\n  \"cinnamon\":2,\n  \"avocado\":3,\n  \"cheddar cheese\":17,\n  \"mozzarella cheese\":6,\n  \"corn tortilla\":2,\n  \"tomatoes\":17,\n  \"barbecue sauce\":2,\n  \"white pepper\":13,\n  \"brown rice\":3,\n  \"almonds\":2,\n  \"eggs\":84,\n  \"margarine\":22,\n  \"butter\":86,\n  \"garlic powder\":9,\n  \"dried thyme\":2,\n  \"salmon\":4,\n  \"cream of mushroom soup\":4,\n  \"whole wheat flour\":2,\n  \"lemon\":4,\n  \"salt\":129,\n  \"black pepper\":11,\n  \"black olives\":2,\n  \"potatoes\":5,\n  \"eggplant\":5,\n  \"dried oregano\":6,\n  \"cornmeal\":2,\n  \"cloves\":2,\n  \"frozen spinach\":4,\n  \"oil\":7,\n  \"chicken\":8,\n  \"blue cheese\":5,\n  \"cucumber\":2,\n  \"dijon mustard\":12,\n  \"soy sauce\":3,\n  \"celery\":12,\n  \"broccoli\":5,\n  \"maple syrup\":2,\n  \"alfalfa sprouts\":3,\n  \"nutmeg\":14,\n  \"egg whites\":5,\n  \"baking soda\":2,\n  \"bread crumbs\":16,\n  \"worcestershire sauce\":12,\n  \"balsamic vinegar\":2,\n  \"dry mustard\":10,\n  \"raw shrimp\":2,\n  \"mayonnaise\":22,\n  \"cumin\":3,\n  \"green onions\":21,\n  \"canned chicken broth\":3,\n  \"olive oil\":16,\n  \"vegetable oil\":11,\n  \"beets\":2,\n  \"skim milk\":6,\n  \"flour\":61,\n  \"kidney beans\":2,\n  \"shortening\":2,\n  \"vinegar\":2,\n  \"parmesan cheese\":32,\n  \"white wine\":17,\n  \"sherry\":3,\n  \"carrots\":5,\n  \"whipping cream\":7,\n  \"chicken breast\":3,\n  \"sugar\":25,\n  \"mushrooms\":22},\n \"oil\":\n {\"elbow macaroni\":4,\n  \"red wine\":33,\n  \"canned tomato sauce\":2,\n  \"bacon\":51,\n  \"cayenne pepper\":76,\n  \"honey\":197,\n  \"lemon juice\":228,\n  \"swiss cheese\":7,\n  \"paprika\":176,\n  \"ground cinnamon\":34,\n  \"bread\":18,\n  \"yeast\":141,\n  \"egg yolks\":36,\n  \"green pepper\":165,\n  \"baking powder\":405,\n  \"saffron\":16,\n  \"sesame oil\":100,\n  \"pine nuts\":15,\n  \"cream of tartar\":17,\n  \"cinnamon\":316,\n  \"avocado\":11,\n  \"cheddar cheese\":32,\n  \"corn tortilla\":4,\n  \"tomatoes\":250,\n  \"white pepper\":54,\n  \"brown rice\":16,\n  \"black beans\":26,\n  \"fresh garlic\":2,\n  \"pinto beans\":6,\n  \"cauliflower\":29,\n  \"butter\":306,\n  \"frozen peas\":16,\n  \"salmon\":5,\n  \"orange juice\":52,\n  \"lemon\":67,\n  \"black pepper\":163,\n  \"tumeric\":16,\n  \"black olives\":13,\n  \"buttermilk\":58,\n  \"powdered sugar\":36,\n  \"hoisin sauce\":23,\n  \"eggplant\":48,\n  \"dried oregano\":35,\n  \"capers\":22,\n  \"cornmeal\":42,\n  \"cloves\":75,\n  \"chicken\":114,\n  \"blue cheese\":5,\n  \"cucumber\":30,\n  \"dry rosemary\":2,\n  \"canola oil\":3,\n  \"dried tarragon\":3,\n  \"strawberries\":5,\n  \"celery\":159,\n  \"alfalfa sprouts\":2,\n  \"cornish game hens\":2,\n  \"fresh cilantro\":10,\n  \"walnuts\":70,\n  \"nutmeg\":117,\n  \"baking soda\":274,\n  \"bread crumbs\":36,\n  \"corn syrup\":3,\n  \"tabasco sauce\":32,\n  \"ginger\":178,\n  \"balsamic vinegar\":10,\n  \"fresh rosemary\":5,\n  \"lime\":13,\n  \"pecans\":42,\n  \"peanut butter\":26,\n  \"frozen corn\":3,\n  \"cumin\":136,\n  \"green onions\":102,\n  \"canned chicken broth\":3,\n  \"beets\":8,\n  \"banana\":11,\n  \"skim milk\":21,\n  \"barley\":9,\n  \"kidney beans\":22,\n  \"shortening\":13,\n  \"parmesan cheese\":62,\n  \"applesauce\":14,\n  \"carrots\":208,\n  \"mushrooms\":162,\n  \"sour cream\":114,\n  \"yogurt\":32,\n  \"cream cheese\":27,\n  \"tofu\":41,\n  \"cherry tomatoes\":9,\n  \"salsa\":16,\n  \"parsley\":213,\n  \"green olives\":6,\n  \"rice vinegar\":14,\n  \"molasses\":47,\n  \"chili powder\":158,\n  \"beer\":34,\n  \"spaghetti\":11,\n  \"raisins\":116,\n  \"red wine vinegar\":41,\n  \"chives\":18,\n  \"garam masala\":32,\n  \"cornstarch\":731,\n  \"dried basil\":23,\n  \"orange\":13,\n  \"radishes\":8,\n  \"catsup\":58,\n  \"ground beef\":66,\n  \"romaine lettuce\":2,\n  \"adzuki beans\":2,\n  \"mozzarella cheese\":29,\n  \"sweet red pepper\":6,\n  \"cumin seeds\":29,\n  \"canned peaches\":2,\n  \"barbecue sauce\":4,\n  \"red pepper flakes\":10,\n  \"eggs\":748,\n  \"almonds\":37,\n  \"margarine\":39,\n  \"fresh ginger\":28,\n  \"kale\":5,\n  \"garlic powder\":124,\n  \"dried thyme\":22,\n  \"cream of mushroom soup\":14,\n  \"low fat milk\":2,\n  \"vanilla\":258,\n  \"whole wheat flour\":89,\n  \"salt\":2800,\n  \"fresh basil\":13,\n  \"bean sprouts\":71,\n  \"sriracha hot chili sauce\":2,\n  \"shiitake mushrooms\":4,\n  \"potatoes\":140,\n  \"almond extract\":20,\n  \"frozen spinach\":9,\n  \"celery seeds\":2,\n  \"allspice\":44,\n  \"dijon mustard\":33,\n  \"soy sauce\":896,\n  \"broccoli\":29,\n  \"chicken legs\":5,\n  \"apples\":54,\n  \"yellow mustard\":2,\n  \"shell macaroni\":3,\n  \"white rice\":5,\n  \"maple syrup\":18,\n  \"sweet potatoes\":15,\n  \"egg whites\":44,\n  \"worcestershire sauce\":119,\n  \"brown sugar\":202,\n  \"lime juice\":45,\n  \"dry mustard\":65,\n  \"raw shrimp\":7,\n  \"white flour\":20,\n  \"blueberries\":11,\n  \"mayonnaise\":31,\n  \"chicken wings\":16,\n  \"fresh thyme\":5,\n  \"vegetable oil\":16,\n  \"olive oil\":52,\n  \"curry powder\":79,\n  \"flour\":957,\n  \"vinegar\":311,\n  \"white wine\":60,\n  \"sherry\":435,\n  \"oats\":23,\n  \"whipping cream\":25,\n  \"chicken breast\":82,\n  \"sugar\":1465},\n \"vegetable oil\":\n {\"canned beef broth\":5,\n  \"red wine\":22,\n  \"elbow macaroni\":6,\n  \"canned tomato sauce\":11,\n  \"bacon\":54,\n  \"brown onions\":2,\n  \"cayenne pepper\":149,\n  \"honey\":227,\n  \"lemon juice\":344,\n  \"swiss cheese\":11,\n  \"paprika\":193,\n  \"bread\":9,\n  \"ground cinnamon\":184,\n  \"yeast\":92,\n  \"egg yolks\":35,\n  \"green pepper\":140,\n  \"baking powder\":527,\n  \"saffron\":8,\n  \"sesame oil\":126,\n  \"pine nuts\":17,\n  \"cinnamon\":232,\n  \"cream of tartar\":20,\n  \"avocado\":13,\n  \"cheddar cheese\":42,\n  \"corn tortilla\":3,\n  \"tomatoes\":196,\n  \"white pepper\":66,\n  \"brown rice\":27,\n  \"black beans\":34,\n  \"fresh garlic\":5,\n  \"pinto beans\":14,\n  \"cauliflower\":14,\n  \"butter\":220,\n  \"frozen peas\":23,\n  \"salmon\":7,\n  \"orange juice\":90,\n  \"black pepper\":202,\n  \"lemon\":60,\n  \"tumeric\":9,\n  \"black olives\":8,\n  \"buttermilk\":97,\n  \"powdered sugar\":49,\n  \"eggplant\":26,\n  \"hoisin sauce\":14,\n  \"dried oregano\":87,\n  \"capers\":34,\n  \"cornmeal\":62,\n  \"cloves\":56,\n  \"oil\":16,\n  \"chicken\":56,\n  \"blue cheese\":4,\n  \"cucumber\":28,\n  \"canola oil\":2,\n  \"flour tortilla\":5,\n  \"dried tarragon\":14,\n  \"strawberries\":9,\n  \"celery\":82,\n  \"cornish game hens\":3,\n  \"alfalfa sprouts\":6,\n  \"fresh cilantro\":54,\n  \"walnuts\":70,\n  \"nutmeg\":93,\n  \"baking soda\":406,\n  \"bread crumbs\":37,\n  \"corn syrup\":2,\n  \"tabasco sauce\":38,\n  \"balsamic vinegar\":23,\n  \"ginger\":110,\n  \"fresh rosemary\":5,\n  \"lime\":24,\n  \"pecans\":25,\n  \"peanut butter\":24,\n  \"frozen corn\":9,\n  \"green onions\":180,\n  \"cumin\":123,\n  \"canned chicken broth\":8,\n  \"beets\":9,\n  \"banana\":17,\n  \"skim milk\":71,\n  \"barley\":4,\n  \"kidney beans\":16,\n  \"shortening\":11,\n  \"applesauce\":26,\n  \"parmesan cheese\":45,\n  \"carrots\":184,\n  \"mushrooms\":111,\n  \"sour cream\":117,\n  \"yogurt\":28,\n  \"tofu\":25,\n  \"cream cheese\":31,\n  \"cherry tomatoes\":18,\n  \"salsa\":26,\n  \"parsley\":117,\n  \"green olives\":6,\n  \"rice vinegar\":38,\n  \"molasses\":65,\n  \"chili powder\":221,\n  \"beer\":31,\n  \"spaghetti\":16,\n  \"raisins\":149,\n  \"red wine vinegar\":101,\n  \"chives\":14,\n  \"garam masala\":35,\n  \"cornstarch\":336,\n  \"dried basil\":46,\n  \"orange\":23,\n  \"radishes\":5,\n  \"catsup\":38,\n  \"ground beef\":59,\n  \"adzuki beans\":4,\n  \"romaine lettuce\":6,\n  \"mozzarella cheese\":16,\n  \"sweet red pepper\":31,\n  \"canned tuna\":2,\n  \"cumin seeds\":35,\n  \"barbecue sauce\":6,\n  \"raw spinach\":2,\n  \"red pepper flakes\":25,\n  \"eggs\":597,\n  \"almonds\":29,\n  \"margarine\":46,\n  \"fresh ginger\":44,\n  \"kale\":5,\n  \"garlic powder\":129,\n  \"dried thyme\":95,\n  \"cream of mushroom soup\":7,\n  \"low fat milk\":3,\n  \"vanilla\":159,\n  \"whole wheat flour\":139,\n  \"salt\":2524,\n  \"fresh basil\":23,\n  \"bean sprouts\":37,\n  \"shiitake mushrooms\":8,\n  \"potatoes\":114,\n  \"almond extract\":21,\n  \"frozen spinach\":7,\n  \"celery seeds\":6,\n  \"allspice\":45,\n  \"dijon mustard\":114,\n  \"soy sauce\":429,\n  \"broccoli\":29,\n  \"chicken legs\":7,\n  \"apples\":28,\n  \"yellow mustard\":2,\n  \"maple syrup\":14,\n  \"white rice\":4,\n  \"sweet potatoes\":17,\n  \"red leaf lettuce\":2,\n  \"egg whites\":109,\n  \"worcestershire sauce\":130,\n  \"brown sugar\":204,\n  \"lime juice\":106,\n  \"dry mustard\":88,\n  \"raw shrimp\":6,\n  \"white flour\":21,\n  \"blueberries\":20,\n  \"mayonnaise\":24,\n  \"chicken wings\":27,\n  \"olive oil\":99,\n  \"fresh thyme\":15,\n  \"curry powder\":88,\n  \"flour\":537,\n  \"vinegar\":127,\n  \"white wine\":40,\n  \"sherry\":34,\n  \"yam\":2,\n  \"oats\":15,\n  \"whipping cream\":31,\n  \"chicken breast\":21,\n  \"sugar\":1133},\n \"pinto beans\":\n {\"sour cream\":6,\n  \"red wine\":2,\n  \"tofu\":2,\n  \"bacon\":10,\n  \"salsa\":10,\n  \"parsley\":6,\n  \"cayenne pepper\":10,\n  \"lemon juice\":8,\n  \"molasses\":3,\n  \"chili powder\":59,\n  \"paprika\":10,\n  \"beer\":5,\n  \"ground cinnamon\":3,\n  \"red wine vinegar\":3,\n  \"green pepper\":19,\n  \"baking powder\":2,\n  \"cornstarch\":2,\n  \"dried basil\":2,\n  \"ground beef\":10,\n  \"cinnamon\":5,\n  \"cheddar cheese\":11,\n  \"mozzarella cheese\":2,\n  \"sweet red pepper\":2,\n  \"tomatoes\":23,\n  \"cumin seeds\":3,\n  \"white pepper\":2,\n  \"brown rice\":2,\n  \"red pepper flakes\":3,\n  \"black beans\":10,\n  \"almonds\":4,\n  \"butter\":7,\n  \"garlic powder\":13,\n  \"kale\":2,\n  \"dried thyme\":3,\n  \"cream of mushroom soup\":2,\n  \"black pepper\":24,\n  \"salt\":64,\n  \"black olives\":4,\n  \"buttermilk\":2,\n  \"potatoes\":2,\n  \"dried oregano\":6,\n  \"cornmeal\":2,\n  \"cloves\":5,\n  \"oil\":6,\n  \"flour tortilla\":2,\n  \"canola oil\":2,\n  \"dijon mustard\":2,\n  \"soy sauce\":4,\n  \"celery\":13,\n  \"maple syrup\":3,\n  \"sweet potatoes\":2,\n  \"nutmeg\":3,\n  \"fresh cilantro\":4,\n  \"egg whites\":2,\n  \"baking soda\":2,\n  \"bread crumbs\":3,\n  \"worcestershire sauce\":4,\n  \"tabasco sauce\":7,\n  \"brown sugar\":6,\n  \"lime juice\":3,\n  \"dry mustard\":4,\n  \"peanut butter\":2,\n  \"frozen corn\":3,\n  \"green onions\":5,\n  \"cumin\":38,\n  \"vegetable oil\":14,\n  \"olive oil\":20,\n  \"skim milk\":2,\n  \"banana\":2,\n  \"flour\":4,\n  \"barley\":2,\n  \"curry powder\":2,\n  \"kidney beans\":20,\n  \"shortening\":3,\n  \"vinegar\":7,\n  \"carrots\":10,\n  \"sugar\":8,\n  \"mushrooms\":5},\n \"lamb shank\":{\"salt\":2, \"lemon juice\":2},\n \"lemon juice\":\n {\"canned beef broth\":2,\n  \"elbow macaroni\":4,\n  \"red wine\":22,\n  \"canned tomato sauce\":2,\n  \"coconut oil\":2,\n  \"bacon\":46,\n  \"cayenne pepper\":137,\n  \"honey\":295,\n  \"swiss cheese\":7,\n  \"zucchini squash\":2,\n  \"paprika\":283,\n  \"bread\":23,\n  \"ground cinnamon\":117,\n  \"yeast\":11,\n  \"egg yolks\":188,\n  \"green pepper\":98,\n  \"baking powder\":206,\n  \"saffron\":12,\n  \"sesame oil\":37,\n  \"cream of tartar\":46,\n  \"pine nuts\":24,\n  \"cinnamon\":299,\n  \"avocado\":34,\n  \"cheddar cheese\":34,\n  \"tomatoes\":194,\n  \"white pepper\":108,\n  \"brown rice\":12,\n  \"black beans\":10,\n  \"fresh garlic\":3,\n  \"pinto beans\":8,\n  \"cauliflower\":15,\n  \"frozen peas\":10,\n  \"butter\":973,\n  \"salmon\":34,\n  \"orange juice\":275,\n  \"lemon\":87,\n  \"black pepper\":258,\n  \"tumeric\":16,\n  \"black olives\":22,\n  \"buttermilk\":34,\n  \"powdered sugar\":92,\n  \"hoisin sauce\":7,\n  \"eggplant\":42,\n  \"dried oregano\":70,\n  \"capers\":71,\n  \"cornmeal\":7,\n  \"cloves\":62,\n  \"oil\":228,\n  \"chicken\":95,\n  \"blue cheese\":14,\n  \"cucumber\":71,\n  \"dry rosemary\":2,\n  \"flour tortilla\":2,\n  \"canola oil\":31,\n  \"dried tarragon\":34,\n  \"strawberries\":71,\n  \"celery\":128,\n  \"alfalfa sprouts\":7,\n  \"cornish game hens\":11,\n  \"fresh peaches\":8,\n  \"fresh cilantro\":21,\n  \"nutmeg\":176,\n  \"walnuts\":86,\n  \"baking soda\":110,\n  \"bread crumbs\":77,\n  \"corn syrup\":9,\n  \"tabasco sauce\":92,\n  \"balsamic vinegar\":47,\n  \"ginger\":109,\n  \"fresh rosemary\":11,\n  \"lime\":7,\n  \"pecans\":41,\n  \"peanut butter\":28,\n  \"frozen corn\":2,\n  \"green onions\":137,\n  \"cumin\":120,\n  \"canned chicken broth\":6,\n  \"beets\":19,\n  \"banana\":22,\n  \"skim milk\":42,\n  \"barley\":5,\n  \"kidney beans\":8,\n  \"shortening\":49,\n  \"parmesan cheese\":77,\n  \"applesauce\":15,\n  \"carrots\":124,\n  \"mushrooms\":136,\n  \"sour cream\":248,\n  \"yogurt\":43,\n  \"brussels sprouts\":2,\n  \"tofu\":29,\n  \"cream cheese\":195,\n  \"cherry tomatoes\":25,\n  \"salsa\":9,\n  \"parsley\":300,\n  \"green olives\":10,\n  \"rice vinegar\":17,\n  \"molasses\":42,\n  \"chili powder\":99,\n  \"beer\":20,\n  \"spaghetti\":9,\n  \"raisins\":109,\n  \"red wine vinegar\":70,\n  \"garam masala\":38,\n  \"chives\":48,\n  \"cornstarch\":341,\n  \"orange\":34,\n  \"dried basil\":54,\n  \"radishes\":20,\n  \"catsup\":78,\n  \"ground beef\":29,\n  \"mozzarella cheese\":11,\n  \"romaine lettuce\":9,\n  \"sweet red pepper\":20,\n  \"canned tuna\":3,\n  \"cumin seeds\":37,\n  \"barbecue sauce\":7,\n  \"red pepper flakes\":27,\n  \"eggs\":525,\n  \"almonds\":40,\n  \"margarine\":184,\n  \"fresh ginger\":34,\n  \"kale\":3,\n  \"garlic powder\":171,\n  \"dried thyme\":56,\n  \"cream of mushroom soup\":13,\n  \"low fat milk\":2,\n  \"vanilla\":257,\n  \"whole wheat flour\":29,\n  \"salt\":2421,\n  \"fresh basil\":31,\n  \"bean sprouts\":7,\n  \"shiitake mushrooms\":8,\n  \"potatoes\":65,\n  \"almond extract\":37,\n  \"frozen spinach\":7,\n  \"canned apricots\":2,\n  \"celery seeds\":10,\n  \"allspice\":42,\n  \"dijon mustard\":150,\n  \"soy sauce\":268,\n  \"broccoli\":16,\n  \"chicken legs\":6,\n  \"apples\":109,\n  \"yellow mustard\":2,\n  \"shell macaroni\":2,\n  \"white rice\":7,\n  \"maple syrup\":37,\n  \"sweet potatoes\":11,\n  \"canned tomato paste\":2,\n  \"egg whites\":151,\n  \"worcestershire sauce\":322,\n  \"dried cilantro\":3,\n  \"brown sugar\":332,\n  \"lime juice\":40,\n  \"dry mustard\":151,\n  \"raw shrimp\":5,\n  \"white flour\":8,\n  \"blueberries\":45,\n  \"mayonnaise\":333,\n  \"chicken wings\":18,\n  \"fresh thyme\":13,\n  \"olive oil\":803,\n  \"vegetable oil\":344,\n  \"flour\":619,\n  \"curry powder\":138,\n  \"vinegar\":131,\n  \"lamb shank\":2,\n  \"white wine\":92,\n  \"sherry\":43,\n  \"oats\":8,\n  \"whipping cream\":138,\n  \"chicken breast\":21,\n  \"sugar\":1688},\n \"garam masala\":\n {\"yogurt\":22,\n  \"tofu\":2,\n  \"cherry tomatoes\":2,\n  \"cayenne pepper\":27,\n  \"honey\":2,\n  \"rice vinegar\":2,\n  \"lemon juice\":38,\n  \"chili powder\":21,\n  \"paprika\":20,\n  \"ground cinnamon\":5,\n  \"raisins\":3,\n  \"green pepper\":4,\n  \"baking powder\":4,\n  \"cornstarch\":2,\n  \"saffron\":8,\n  \"ground beef\":3,\n  \"cinnamon\":3,\n  \"tomatoes\":43,\n  \"cumin seeds\":33,\n  \"white pepper\":4,\n  \"eggs\":4,\n  \"almonds\":5,\n  \"margarine\":3,\n  \"cauliflower\":10,\n  \"fresh ginger\":18,\n  \"frozen peas\":4,\n  \"butter\":12,\n  \"garlic powder\":3,\n  \"whole wheat flour\":3,\n  \"lemon\":7,\n  \"salt\":143,\n  \"black pepper\":13,\n  \"tumeric\":8,\n  \"potatoes\":32,\n  \"buttermilk\":3,\n  \"eggplant\":3,\n  \"cloves\":8,\n  \"frozen spinach\":3,\n  \"oil\":32,\n  \"chicken\":8,\n  \"cucumber\":2,\n  \"allspice\":2,\n  \"canola oil\":5,\n  \"dijon mustard\":2,\n  \"soy sauce\":8,\n  \"broccoli\":2,\n  \"fresh peaches\":2,\n  \"walnuts\":3,\n  \"fresh cilantro\":3,\n  \"nutmeg\":5,\n  \"baking soda\":2,\n  \"worcestershire sauce\":2,\n  \"brown sugar\":6,\n  \"ginger\":29,\n  \"dry mustard\":4,\n  \"lime\":3,\n  \"peanut butter\":2,\n  \"mayonnaise\":2,\n  \"green onions\":5,\n  \"cumin\":19,\n  \"frozen corn\":2,\n  \"vegetable oil\":35,\n  \"olive oil\":4,\n  \"flour\":3,\n  \"curry powder\":7,\n  \"kidney beans\":3,\n  \"vinegar\":5,\n  \"carrots\":5,\n  \"whipping cream\":3,\n  \"chicken breast\":2,\n  \"mushrooms\":4,\n  \"sugar\":18},\n \"tumeric\":\n {\"sour cream\":2,\n  \"yogurt\":6,\n  \"canned beef broth\":2,\n  \"tofu\":2,\n  \"parsley\":3,\n  \"cayenne pepper\":13,\n  \"lemon juice\":16,\n  \"honey\":12,\n  \"molasses\":2,\n  \"chili powder\":14,\n  \"paprika\":12,\n  \"ground cinnamon\":2,\n  \"raisins\":3,\n  \"red wine vinegar\":2,\n  \"garam masala\":8,\n  \"green pepper\":7,\n  \"cornstarch\":4,\n  \"baking powder\":3,\n  \"saffron\":3,\n  \"sesame oil\":3,\n  \"radishes\":2,\n  \"catsup\":3,\n  \"cream of tartar\":3,\n  \"cinnamon\":18,\n  \"cheddar cheese\":4,\n  \"sweet red pepper\":2,\n  \"tomatoes\":13,\n  \"cumin seeds\":12,\n  \"white pepper\":3,\n  \"red pepper flakes\":2,\n  \"eggs\":2,\n  \"fresh garlic\":2,\n  \"margarine\":4,\n  \"fresh ginger\":3,\n  \"cauliflower\":4,\n  \"frozen peas\":4,\n  \"butter\":7,\n  \"garlic powder\":6,\n  \"salt\":58,\n  \"lemon\":5,\n  \"black pepper\":14,\n  \"bean sprouts\":2,\n  \"potatoes\":11,\n  \"buttermilk\":2,\n  \"dried oregano\":3,\n  \"eggplant\":4,\n  \"cloves\":10,\n  \"oil\":16,\n  \"chicken\":5,\n  \"cucumber\":5,\n  \"canola oil\":3,\n  \"allspice\":9,\n  \"celery seeds\":5,\n  \"soy sauce\":6,\n  \"celery\":4,\n  \"nutmeg\":6,\n  \"baking soda\":3,\n  \"brown sugar\":7,\n  \"balsamic vinegar\":2,\n  \"ginger\":20,\n  \"dry mustard\":13,\n  \"white flour\":2,\n  \"mayonnaise\":2,\n  \"cumin\":21,\n  \"green onions\":2,\n  \"chicken wings\":2,\n  \"vegetable oil\":9,\n  \"olive oil\":14,\n  \"flour\":7,\n  \"curry powder\":7,\n  \"kidney beans\":2,\n  \"vinegar\":9,\n  \"carrots\":5,\n  \"whipping cream\":2,\n  \"mushrooms\":5,\n  \"sugar\":25},\n \"lemon\":\n {\"red wine\":10,\n  \"canned beef broth\":3,\n  \"bacon\":18,\n  \"cayenne pepper\":34,\n  \"honey\":73,\n  \"lemon juice\":87,\n  \"swiss cheese\":4,\n  \"paprika\":63,\n  \"bread\":5,\n  \"ground cinnamon\":25,\n  \"yeast\":10,\n  \"egg yolks\":71,\n  \"green pepper\":22,\n  \"baking powder\":55,\n  \"saffron\":7,\n  \"sesame oil\":4,\n  \"pine nuts\":8,\n  \"cinnamon\":59,\n  \"cream of tartar\":6,\n  \"avocado\":8,\n  \"cheddar cheese\":2,\n  \"tomatoes\":55,\n  \"white pepper\":18,\n  \"brown rice\":2,\n  \"black beans\":5,\n  \"cauliflower\":4,\n  \"butter\":301,\n  \"frozen peas\":4,\n  \"salmon\":6,\n  \"orange juice\":27,\n  \"black pepper\":54,\n  \"tumeric\":5,\n  \"black olives\":9,\n  \"buttermilk\":4,\n  \"powdered sugar\":14,\n  \"hoisin sauce\":4,\n  \"eggplant\":13,\n  \"dried oregano\":16,\n  \"capers\":28,\n  \"cornmeal\":7,\n  \"cloves\":34,\n  \"oil\":67,\n  \"chicken\":33,\n  \"blue cheese\":2,\n  \"cucumber\":16,\n  \"canola oil\":2,\n  \"dried tarragon\":6,\n  \"strawberries\":12,\n  \"celery\":28,\n  \"cornish game hens\":2,\n  \"alfalfa sprouts\":3,\n  \"fresh peaches\":2,\n  \"fresh cilantro\":5,\n  \"walnuts\":12,\n  \"nutmeg\":39,\n  \"bread crumbs\":23,\n  \"baking soda\":19,\n  \"tabasco sauce\":20,\n  \"balsamic vinegar\":11,\n  \"ginger\":27,\n  \"fresh rosemary\":2,\n  \"lime\":26,\n  \"pecans\":6,\n  \"green onions\":28,\n  \"cumin\":23,\n  \"canned chicken broth\":3,\n  \"skim milk\":4,\n  \"beets\":5,\n  \"banana\":3,\n  \"barley\":2,\n  \"kidney beans\":2,\n  \"shortening\":10,\n  \"applesauce\":2,\n  \"parmesan cheese\":27,\n  \"carrots\":34,\n  \"cashew\":2,\n  \"mushrooms\":29,\n  \"sour cream\":35,\n  \"yogurt\":6,\n  \"tofu\":2,\n  \"cream cheese\":8,\n  \"cherry tomatoes\":4,\n  \"parsley\":94,\n  \"green olives\":11,\n  \"rice vinegar\":3,\n  \"molasses\":12,\n  \"chili powder\":23,\n  \"beer\":10,\n  \"spaghetti\":5,\n  \"raisins\":38,\n  \"red wine vinegar\":19,\n  \"chives\":11,\n  \"garam masala\":7,\n  \"cornstarch\":39,\n  \"dried basil\":9,\n  \"orange\":85,\n  \"radishes\":2,\n  \"catsup\":14,\n  \"ground beef\":6,\n  \"romaine lettuce\":2,\n  \"mozzarella cheese\":3,\n  \"adzuki beans\":2,\n  \"sweet red pepper\":2,\n  \"cumin seeds\":4,\n  \"barbecue sauce\":2,\n  \"red pepper flakes\":4,\n  \"almonds\":18,\n  \"eggs\":138,\n  \"margarine\":24,\n  \"fresh ginger\":10,\n  \"garlic powder\":21,\n  \"dried thyme\":15,\n  \"vanilla\":33,\n  \"salt\":483,\n  \"fresh basil\":11,\n  \"bean sprouts\":3,\n  \"shiitake mushrooms\":3,\n  \"potatoes\":17,\n  \"almond extract\":5,\n  \"frozen spinach\":3,\n  \"allspice\":20,\n  \"celery seeds\":3,\n  \"dijon mustard\":21,\n  \"soy sauce\":37,\n  \"broccoli\":4,\n  \"chicken legs\":2,\n  \"apples\":27,\n  \"yellow mustard\":3,\n  \"maple syrup\":3,\n  \"sweet potatoes\":2,\n  \"white rice\":3,\n  \"egg whites\":31,\n  \"worcestershire sauce\":75,\n  \"brown sugar\":55,\n  \"lime juice\":2,\n  \"dry mustard\":25,\n  \"raw shrimp\":2,\n  \"blueberries\":5,\n  \"mayonnaise\":44,\n  \"chicken wings\":4,\n  \"fresh thyme\":6,\n  \"vegetable oil\":60,\n  \"olive oil\":236,\n  \"curry powder\":16,\n  \"flour\":191,\n  \"vinegar\":41,\n  \"white wine\":40,\n  \"sherry\":11,\n  \"whipping cream\":29,\n  \"chicken breast\":5,\n  \"sugar\":379},\n \"capers\":\n {\"red wine\":7,\n  \"elbow macaroni\":2,\n  \"canned tomato sauce\":2,\n  \"bacon\":5,\n  \"cayenne pepper\":9,\n  \"lemon juice\":71,\n  \"honey\":4,\n  \"paprika\":20,\n  \"bread\":5,\n  \"yeast\":2,\n  \"egg yolks\":14,\n  \"green pepper\":15,\n  \"baking powder\":2,\n  \"saffron\":6,\n  \"cinnamon\":10,\n  \"pine nuts\":10,\n  \"avocado\":4,\n  \"cheddar cheese\":3,\n  \"tomatoes\":37,\n  \"white pepper\":7,\n  \"black beans\":2,\n  \"cauliflower\":4,\n  \"butter\":58,\n  \"salmon\":2,\n  \"orange juice\":4,\n  \"black pepper\":23,\n  \"lemon\":28,\n  \"black olives\":28,\n  \"buttermilk\":3,\n  \"eggplant\":11,\n  \"dried oregano\":22,\n  \"cloves\":6,\n  \"oil\":22,\n  \"chicken\":10,\n  \"blue cheese\":2,\n  \"cucumber\":7,\n  \"dried tarragon\":5,\n  \"celery\":13,\n  \"alfalfa sprouts\":2,\n  \"nutmeg\":6,\n  \"bread crumbs\":5,\n  \"tabasco sauce\":9,\n  \"balsamic vinegar\":19,\n  \"ginger\":2,\n  \"lime\":3,\n  \"pecans\":2,\n  \"green onions\":9,\n  \"cumin\":8,\n  \"skim milk\":2,\n  \"beets\":4,\n  \"parmesan cheese\":15,\n  \"carrots\":8,\n  \"mushrooms\":12,\n  \"sour cream\":14,\n  \"yogurt\":2,\n  \"cream cheese\":7,\n  \"tofu\":2,\n  \"cherry tomatoes\":2,\n  \"parsley\":50,\n  \"green olives\":22,\n  \"rice vinegar\":3,\n  \"molasses\":2,\n  \"beer\":2,\n  \"chili powder\":3,\n  \"spaghetti\":5,\n  \"raisins\":9,\n  \"red wine vinegar\":41,\n  \"chives\":7,\n  \"cornstarch\":11,\n  \"dried basil\":14,\n  \"orange\":3,\n  \"catsup\":2,\n  \"ground beef\":11,\n  \"mozzarella cheese\":6,\n  \"sweet red pepper\":2,\n  \"canned tuna\":2,\n  \"cumin seeds\":2,\n  \"red pepper flakes\":8,\n  \"almonds\":2,\n  \"eggs\":14,\n  \"margarine\":5,\n  \"garlic powder\":6,\n  \"dried thyme\":6,\n  \"salt\":151,\n  \"fresh basil\":10,\n  \"potatoes\":4,\n  \"allspice\":3,\n  \"dijon mustard\":24,\n  \"soy sauce\":3,\n  \"broccoli\":2,\n  \"white rice\":2,\n  \"worcestershire sauce\":17,\n  \"brown sugar\":6,\n  \"lime juice\":9,\n  \"dry mustard\":10,\n  \"mayonnaise\":32,\n  \"olive oil\":187,\n  \"vegetable oil\":34,\n  \"fresh thyme\":3,\n  \"flour\":30,\n  \"curry powder\":3,\n  \"vinegar\":29,\n  \"white wine\":21,\n  \"sherry\":2,\n  \"whipping cream\":5,\n  \"chicken breast\":3,\n  \"sugar\":41},\n \"canned apricots\":\n {\"olive oil\":2,\n  \"baking powder\":2,\n  \"sour cream\":2,\n  \"flour\":2,\n  \"dijon mustard\":2,\n  \"ground cinnamon\":3,\n  \"vanilla\":2,\n  \"honey\":2,\n  \"salt\":2,\n  \"lemon juice\":2},\n \"brown onions\":\n {\"brown sugar\":2,\n  \"ginger\":2,\n  \"cinnamon\":2,\n  \"potatoes\":3,\n  \"mozzarella cheese\":2,\n  \"cayenne pepper\":3,\n  \"cloves\":2,\n  \"cumin seeds\":2,\n  \"green onions\":2,\n  \"vegetable oil\":2,\n  \"olive oil\":2,\n  \"flour\":2,\n  \"eggs\":2,\n  \"paprika\":5,\n  \"black beans\":2,\n  \"fresh ginger\":2,\n  \"butter\":3,\n  \"nutmeg\":2,\n  \"salt\":3,\n  \"sugar\":2},\n \"cream of mushroom soup\":\n {\"sour cream\":51,\n  \"elbow macaroni\":7,\n  \"red wine\":3,\n  \"cream cheese\":5,\n  \"frozen french fries\":3,\n  \"bacon\":18,\n  \"cayenne pepper\":4,\n  \"parsley\":17,\n  \"green olives\":2,\n  \"lemon juice\":13,\n  \"swiss cheese\":4,\n  \"chili powder\":15,\n  \"paprika\":28,\n  \"beer\":2,\n  \"spaghetti\":3,\n  \"bread\":5,\n  \"red wine vinegar\":2,\n  \"yeast\":2,\n  \"green pepper\":23,\n  \"cornstarch\":5,\n  \"dried basil\":3,\n  \"catsup\":5,\n  \"ground beef\":46,\n  \"cheddar cheese\":33,\n  \"mozzarella cheese\":4,\n  \"tomatoes\":4,\n  \"white pepper\":4,\n  \"eggs\":38,\n  \"pinto beans\":2,\n  \"margarine\":19,\n  \"butter\":56,\n  \"frozen peas\":6,\n  \"garlic powder\":18,\n  \"dried thyme\":2,\n  \"salmon\":5,\n  \"orange juice\":2,\n  \"salt\":93,\n  \"black pepper\":13,\n  \"bean sprouts\":3,\n  \"potatoes\":21,\n  \"buttermilk\":3,\n  \"dried oregano\":2,\n  \"frozen spinach\":2,\n  \"oil\":14,\n  \"chicken\":29,\n  \"cucumber\":2,\n  \"allspice\":2,\n  \"dijon mustard\":2,\n  \"soy sauce\":10,\n  \"celery\":35,\n  \"broccoli\":4,\n  \"sweet potatoes\":2,\n  \"white rice\":2,\n  \"nutmeg\":4,\n  \"bread crumbs\":11,\n  \"worcestershire sauce\":23,\n  \"tabasco sauce\":2,\n  \"brown sugar\":3,\n  \"dry mustard\":9,\n  \"peanut butter\":2,\n  \"mayonnaise\":25,\n  \"cumin\":4,\n  \"green onions\":13,\n  \"frozen corn\":2,\n  \"olive oil\":3,\n  \"vegetable oil\":7,\n  \"skim milk\":5,\n  \"flour\":31,\n  \"curry powder\":9,\n  \"kidney beans\":2,\n  \"shortening\":6,\n  \"parmesan cheese\":17,\n  \"white wine\":10,\n  \"carrots\":13,\n  \"sherry\":3,\n  \"whipping cream\":3,\n  \"chicken breast\":2,\n  \"sugar\":3,\n  \"mushrooms\":30},\n \"lime juice\":\n {\"red wine\":7,\n  \"bacon\":4,\n  \"cayenne pepper\":32,\n  \"lemon juice\":40,\n  \"honey\":68,\n  \"paprika\":27,\n  \"ground cinnamon\":16,\n  \"yeast\":2,\n  \"egg yolks\":13,\n  \"green pepper\":12,\n  \"baking powder\":14,\n  \"saffron\":2,\n  \"sesame oil\":15,\n  \"cream of tartar\":7,\n  \"cinnamon\":21,\n  \"pine nuts\":4,\n  \"avocado\":26,\n  \"cheddar cheese\":4,\n  \"corn tortilla\":2,\n  \"tomatoes\":75,\n  \"white pepper\":18,\n  \"black beans\":19,\n  \"pinto beans\":3,\n  \"fresh garlic\":2,\n  \"butter\":43,\n  \"orange juice\":48,\n  \"lemon\":2,\n  \"black pepper\":36,\n  \"black olives\":4,\n  \"buttermilk\":5,\n  \"powdered sugar\":7,\n  \"eggplant\":2,\n  \"dried oregano\":11,\n  \"capers\":9,\n  \"cloves\":9,\n  \"oil\":45,\n  \"chicken\":12,\n  \"cucumber\":15,\n  \"flour tortilla\":2,\n  \"canola oil\":13,\n  \"dried tarragon\":3,\n  \"strawberries\":6,\n  \"celery\":6,\n  \"cornish game hens\":4,\n  \"fresh peaches\":2,\n  \"walnuts\":6,\n  \"nutmeg\":12,\n  \"fresh cilantro\":59,\n  \"bread crumbs\":2,\n  \"tabasco sauce\":16,\n  \"balsamic vinegar\":5,\n  \"ginger\":26,\n  \"fresh rosemary\":2,\n  \"lime\":10,\n  \"pecans\":4,\n  \"peanut butter\":4,\n  \"green onions\":60,\n  \"cumin\":52,\n  \"frozen corn\":7,\n  \"canned chicken broth\":2,\n  \"banana\":5,\n  \"skim milk\":4,\n  \"shortening\":4,\n  \"parmesan cheese\":5,\n  \"carrots\":17,\n  \"mushrooms\":4,\n  \"sour cream\":28,\n  \"yogurt\":9,\n  \"tofu\":3,\n  \"cream cheese\":11,\n  \"cherry tomatoes\":5,\n  \"salsa\":19,\n  \"parsley\":17,\n  \"green olives\":5,\n  \"rice vinegar\":10,\n  \"molasses\":6,\n  \"beer\":6,\n  \"chili powder\":58,\n  \"spaghetti\":2,\n  \"raisins\":6,\n  \"red wine vinegar\":22,\n  \"chives\":5,\n  \"cornstarch\":24,\n  \"dried basil\":3,\n  \"orange\":4,\n  \"radishes\":3,\n  \"catsup\":6,\n  \"ground beef\":2,\n  \"adzuki beans\":2,\n  \"romaine lettuce\":2,\n  \"sweet red pepper\":8,\n  \"cumin seeds\":9,\n  \"canned peaches\":3,\n  \"barbecue sauce\":2,\n  \"red pepper flakes\":9,\n  \"eggs\":28,\n  \"almonds\":4,\n  \"margarine\":12,\n  \"fresh ginger\":17,\n  \"garlic powder\":18,\n  \"dried thyme\":6,\n  \"low fat milk\":2,\n  \"vanilla\":6,\n  \"salt\":348,\n  \"fresh basil\":4,\n  \"bean sprouts\":12,\n  \"potatoes\":7,\n  \"celery seeds\":2,\n  \"allspice\":7,\n  \"dijon mustard\":22,\n  \"soy sauce\":62,\n  \"broccoli\":2,\n  \"apples\":5,\n  \"sweet potatoes\":2,\n  \"white rice\":3,\n  \"maple syrup\":10,\n  \"egg whites\":9,\n  \"worcestershire sauce\":21,\n  \"dried cilantro\":4,\n  \"brown sugar\":46,\n  \"dry mustard\":8,\n  \"raw shrimp\":3,\n  \"white flour\":3,\n  \"blueberries\":5,\n  \"mayonnaise\":17,\n  \"chicken wings\":8,\n  \"fresh thyme\":3,\n  \"olive oil\":148,\n  \"vegetable oil\":106,\n  \"curry powder\":22,\n  \"flour\":25,\n  \"vinegar\":14,\n  \"white wine\":12,\n  \"sherry\":2,\n  \"whipping cream\":10,\n  \"chicken breast\":7,\n  \"sugar\":204},\n \"green olives\":\n {\"sour cream\":3,\n  \"cream cheese\":2,\n  \"bacon\":2,\n  \"salsa\":2,\n  \"cayenne pepper\":2,\n  \"parsley\":23,\n  \"honey\":2,\n  \"lemon juice\":10,\n  \"swiss cheese\":2,\n  \"chili powder\":5,\n  \"paprika\":8,\n  \"spaghetti\":3,\n  \"bread\":2,\n  \"ground cinnamon\":3,\n  \"raisins\":7,\n  \"red wine vinegar\":5,\n  \"chives\":2,\n  \"egg yolks\":2,\n  \"green pepper\":10,\n  \"saffron\":4,\n  \"catsup\":2,\n  \"pine nuts\":4,\n  \"ground beef\":9,\n  \"cinnamon\":6,\n  \"avocado\":3,\n  \"mozzarella cheese\":2,\n  \"tomatoes\":16,\n  \"cumin seeds\":2,\n  \"white pepper\":2,\n  \"brown rice\":2,\n  \"almonds\":3,\n  \"eggs\":10,\n  \"black beans\":3,\n  \"margarine\":2,\n  \"cauliflower\":3,\n  \"butter\":12,\n  \"garlic powder\":5,\n  \"cream of mushroom soup\":2,\n  \"orange juice\":3,\n  \"salt\":39,\n  \"black pepper\":9,\n  \"lemon\":11,\n  \"black olives\":21,\n  \"potatoes\":4,\n  \"dried oregano\":3,\n  \"eggplant\":5,\n  \"capers\":22,\n  \"cloves\":3,\n  \"oil\":6,\n  \"chicken\":5,\n  \"blue cheese\":2,\n  \"cucumber\":2,\n  \"allspice\":2,\n  \"dijon mustard\":2,\n  \"dried tarragon\":3,\n  \"celery\":10,\n  \"broccoli\":3,\n  \"yellow mustard\":2,\n  \"walnuts\":2,\n  \"nutmeg\":3,\n  \"fresh cilantro\":2,\n  \"bread crumbs\":2,\n  \"worcestershire sauce\":6,\n  \"tabasco sauce\":2,\n  \"ginger\":2,\n  \"balsamic vinegar\":2,\n  \"lime juice\":5,\n  \"lime\":3,\n  \"mayonnaise\":8,\n  \"green onions\":10,\n  \"cumin\":4,\n  \"olive oil\":47,\n  \"vegetable oil\":6,\n  \"fresh thyme\":2,\n  \"beets\":2,\n  \"flour\":7,\n  \"curry powder\":5,\n  \"vinegar\":6,\n  \"parmesan cheese\":4,\n  \"white wine\":6,\n  \"sherry\":3,\n  \"carrots\":4,\n  \"mushrooms\":10,\n  \"sugar\":15},\n \"chicken\":\n {\"elbow macaroni\":2,\n  \"red wine\":10,\n  \"canned tomato sauce\":2,\n  \"bacon\":14,\n  \"cayenne pepper\":29,\n  \"honey\":32,\n  \"lemon juice\":95,\n  \"zucchini squash\":2,\n  \"swiss cheese\":8,\n  \"paprika\":76,\n  \"ground cinnamon\":16,\n  \"bread\":4,\n  \"yeast\":2,\n  \"egg yolks\":9,\n  \"green pepper\":47,\n  \"baking powder\":13,\n  \"saffron\":23,\n  \"sesame oil\":15,\n  \"pine nuts\":5,\n  \"cinnamon\":27,\n  \"avocado\":4,\n  \"cheddar cheese\":26,\n  \"corn tortilla\":2,\n  \"tomatoes\":53,\n  \"white pepper\":9,\n  \"brown rice\":3,\n  \"black beans\":2,\n  \"cauliflower\":3,\n  \"frozen peas\":6,\n  \"butter\":146,\n  \"orange juice\":15,\n  \"black pepper\":61,\n  \"lemon\":33,\n  \"tumeric\":5,\n  \"black olives\":5,\n  \"buttermilk\":5,\n  \"eggplant\":7,\n  \"hoisin sauce\":4,\n  \"dried oregano\":15,\n  \"capers\":10,\n  \"cornmeal\":3,\n  \"cloves\":29,\n  \"oil\":114,\n  \"cucumber\":4,\n  \"canola oil\":2,\n  \"dried tarragon\":4,\n  \"celery\":93,\n  \"fresh cilantro\":3,\n  \"walnuts\":8,\n  \"nutmeg\":17,\n  \"baking soda\":2,\n  \"bread crumbs\":10,\n  \"tabasco sauce\":4,\n  \"balsamic vinegar\":4,\n  \"ginger\":36,\n  \"fresh rosemary\":6,\n  \"lime\":7,\n  \"pecans\":6,\n  \"peanut butter\":3,\n  \"green onions\":42,\n  \"cumin\":25,\n  \"frozen corn\":3,\n  \"skim milk\":7,\n  \"barley\":5,\n  \"kidney beans\":3,\n  \"shortening\":8,\n  \"parmesan cheese\":28,\n  \"carrots\":59,\n  \"mushrooms\":59,\n  \"sour cream\":40,\n  \"yogurt\":14,\n  \"brussels sprouts\":2,\n  \"cream cheese\":4,\n  \"salsa\":6,\n  \"parsley\":70,\n  \"green olives\":5,\n  \"rice vinegar\":3,\n  \"molasses\":2,\n  \"beer\":6,\n  \"chili powder\":18,\n  \"spaghetti\":10,\n  \"raisins\":19,\n  \"red wine vinegar\":13,\n  \"chives\":5,\n  \"garam masala\":8,\n  \"cornstarch\":62,\n  \"dried basil\":11,\n  \"orange\":5,\n  \"catsup\":12,\n  \"radishes\":4,\n  \"ground beef\":5,\n  \"mozzarella cheese\":6,\n  \"sweet red pepper\":4,\n  \"cumin seeds\":8,\n  \"barbecue sauce\":3,\n  \"red pepper flakes\":4,\n  \"eggs\":46,\n  \"almonds\":27,\n  \"margarine\":27,\n  \"fresh ginger\":8,\n  \"kale\":2,\n  \"garlic powder\":42,\n  \"dried thyme\":14,\n  \"cream of mushroom soup\":29,\n  \"whole wheat flour\":2,\n  \"salt\":414,\n  \"fresh basil\":6,\n  \"bean sprouts\":10,\n  \"potatoes\":25,\n  \"celery seeds\":2,\n  \"allspice\":13,\n  \"dijon mustard\":14,\n  \"soy sauce\":73,\n  \"broccoli\":11,\n  \"apples\":7,\n  \"maple syrup\":3,\n  \"sweet potatoes\":5,\n  \"white rice\":3,\n  \"egg whites\":3,\n  \"worcestershire sauce\":22,\n  \"dried cilantro\":2,\n  \"brown sugar\":21,\n  \"lime juice\":12,\n  \"dry mustard\":11,\n  \"white flour\":2,\n  \"mayonnaise\":38,\n  \"chicken wings\":2,\n  \"vegetable oil\":56,\n  \"olive oil\":96,\n  \"fresh thyme\":4,\n  \"flour\":131,\n  \"curry powder\":36,\n  \"vinegar\":29,\n  \"white wine\":31,\n  \"sherry\":28,\n  \"whipping cream\":8,\n  \"chicken breast\":2,\n  \"sugar\":69},\n \"raisins\":\n {\"red wine\":8,\n  \"canned tomato sauce\":4,\n  \"bacon\":5,\n  \"cayenne pepper\":25,\n  \"lemon juice\":109,\n  \"honey\":188,\n  \"paprika\":14,\n  \"ground cinnamon\":223,\n  \"bread\":16,\n  \"yeast\":91,\n  \"egg yolks\":35,\n  \"green pepper\":18,\n  \"baking powder\":483,\n  \"saffron\":4,\n  \"cream of tartar\":7,\n  \"cinnamon\":746,\n  \"pine nuts\":29,\n  \"cheddar cheese\":2,\n  \"corn tortilla\":3,\n  \"tomatoes\":32,\n  \"white pepper\":7,\n  \"brown rice\":7,\n  \"fresh garlic\":2,\n  \"cauliflower\":2,\n  \"butter\":451,\n  \"frozen peas\":3,\n  \"salmon\":2,\n  \"orange juice\":105,\n  \"lemon\":38,\n  \"black pepper\":23,\n  \"tumeric\":3,\n  \"black olives\":4,\n  \"buttermilk\":81,\n  \"powdered sugar\":40,\n  \"dried oregano\":10,\n  \"eggplant\":5,\n  \"capers\":9,\n  \"cornmeal\":14,\n  \"cloves\":184,\n  \"oil\":116,\n  \"chicken\":19,\n  \"cucumber\":2,\n  \"dry rosemary\":2,\n  \"canola oil\":16,\n  \"dried tarragon\":2,\n  \"strawberries\":3,\n  \"celery\":27,\n  \"cornish game hens\":2,\n  \"fresh cilantro\":2,\n  \"walnuts\":136,\n  \"nutmeg\":308,\n  \"bread crumbs\":14,\n  \"baking soda\":529,\n  \"tabasco sauce\":5,\n  \"corn syrup\":23,\n  \"balsamic vinegar\":7,\n  \"ginger\":76,\n  \"fresh rosemary\":2,\n  \"lime\":7,\n  \"pecans\":64,\n  \"peanut butter\":28,\n  \"green onions\":10,\n  \"cumin\":29,\n  \"beets\":3,\n  \"skim milk\":52,\n  \"banana\":9,\n  \"barley\":5,\n  \"kidney beans\":4,\n  \"shortening\":137,\n  \"applesauce\":71,\n  \"parmesan cheese\":5,\n  \"carrots\":61,\n  \"mushrooms\":7,\n  \"sour cream\":51,\n  \"yogurt\":11,\n  \"brussels sprouts\":3,\n  \"tofu\":6,\n  \"cream cheese\":25,\n  \"cherry tomatoes\":2,\n  \"salsa\":3,\n  \"parsley\":23,\n  \"green olives\":7,\n  \"molasses\":97,\n  \"chili powder\":21,\n  \"beer\":4,\n  \"spaghetti\":3,\n  \"red wine vinegar\":18,\n  \"garam masala\":3,\n  \"cornstarch\":47,\n  \"dried basil\":3,\n  \"orange\":41,\n  \"catsup\":4,\n  \"radishes\":2,\n  \"ground beef\":15,\n  \"romaine lettuce\":3,\n  \"sweet red pepper\":4,\n  \"cumin seeds\":8,\n  \"red pepper flakes\":4,\n  \"almonds\":80,\n  \"eggs\":559,\n  \"margarine\":125,\n  \"fresh ginger\":5,\n  \"garlic powder\":7,\n  \"dried thyme\":5,\n  \"low fat milk\":4,\n  \"vanilla\":381,\n  \"whole wheat flour\":111,\n  \"salt\":1175,\n  \"fresh basil\":2,\n  \"potatoes\":7,\n  \"almond extract\":14,\n  \"frozen spinach\":3,\n  \"allspice\":103,\n  \"dijon mustard\":7,\n  \"soy sauce\":8,\n  \"broccoli\":3,\n  \"apples\":110,\n  \"maple syrup\":30,\n  \"sweet potatoes\":17,\n  \"white rice\":5,\n  \"egg whites\":74,\n  \"worcestershire sauce\":10,\n  \"brown sugar\":374,\n  \"lime juice\":6,\n  \"dry mustard\":11,\n  \"white flour\":16,\n  \"blueberries\":5,\n  \"mayonnaise\":20,\n  \"fresh thyme\":3,\n  \"olive oil\":74,\n  \"vegetable oil\":149,\n  \"flour\":590,\n  \"curry powder\":67,\n  \"vinegar\":53,\n  \"white wine\":11,\n  \"sherry\":8,\n  \"oats\":39,\n  \"whipping cream\":21,\n  \"chicken breast\":3,\n  \"sugar\":982},\n \"cream cheese\":\n {\"red wine\":2,\n  \"elbow macaroni\":2,\n  \"bacon\":14,\n  \"cayenne pepper\":20,\n  \"lemon juice\":195,\n  \"honey\":26,\n  \"swiss cheese\":7,\n  \"paprika\":33,\n  \"camembert cheese\":4,\n  \"ground cinnamon\":24,\n  \"bread\":8,\n  \"yeast\":4,\n  \"egg yolks\":39,\n  \"green pepper\":19,\n  \"baking powder\":83,\n  \"cream of tartar\":6,\n  \"pine nuts\":8,\n  \"cinnamon\":105,\n  \"avocado\":8,\n  \"cheddar cheese\":33,\n  \"tomatoes\":16,\n  \"white pepper\":20,\n  \"black beans\":4,\n  \"cauliflower\":3,\n  \"butter\":451,\n  \"salmon\":9,\n  \"orange juice\":29,\n  \"black pepper\":24,\n  \"lemon\":8,\n  \"black olives\":26,\n  \"buttermilk\":17,\n  \"powdered sugar\":153,\n  \"dried oregano\":6,\n  \"capers\":7,\n  \"cornmeal\":4,\n  \"cloves\":8,\n  \"oil\":27,\n  \"chicken\":4,\n  \"blue cheese\":26,\n  \"cucumber\":7,\n  \"dry rosemary\":2,\n  \"dried tarragon\":2,\n  \"strawberries\":21,\n  \"celery\":20,\n  \"alfalfa sprouts\":3,\n  \"cornish game hens\":2,\n  \"fresh peaches\":2,\n  \"fresh cilantro\":3,\n  \"nutmeg\":42,\n  \"walnuts\":49,\n  \"baking soda\":69,\n  \"bread crumbs\":16,\n  \"tabasco sauce\":24,\n  \"corn syrup\":4,\n  \"ginger\":14,\n  \"fresh rosemary\":2,\n  \"lime\":2,\n  \"pecans\":63,\n  \"peanut butter\":21,\n  \"green onions\":53,\n  \"cumin\":12,\n  \"skim milk\":5,\n  \"beets\":3,\n  \"banana\":3,\n  \"shortening\":16,\n  \"applesauce\":2,\n  \"parmesan cheese\":36,\n  \"carrots\":17,\n  \"mushrooms\":21,\n  \"sour cream\":274,\n  \"yogurt\":2,\n  \"cherry tomatoes\":4,\n  \"salsa\":10,\n  \"parsley\":28,\n  \"green olives\":2,\n  \"molasses\":3,\n  \"beer\":3,\n  \"chili powder\":12,\n  \"raisins\":25,\n  \"red wine vinegar\":4,\n  \"chives\":29,\n  \"cornstarch\":82,\n  \"orange\":4,\n  \"dried basil\":6,\n  \"radishes\":5,\n  \"catsup\":6,\n  \"ground beef\":9,\n  \"mozzarella cheese\":10,\n  \"sweet red pepper\":3,\n  \"red pepper flakes\":2,\n  \"eggs\":454,\n  \"almonds\":31,\n  \"margarine\":125,\n  \"garlic powder\":55,\n  \"dried thyme\":3,\n  \"cream of mushroom soup\":5,\n  \"low fat milk\":2,\n  \"vanilla\":419,\n  \"whole wheat flour\":5,\n  \"salt\":399,\n  \"fresh basil\":7,\n  \"potatoes\":15,\n  \"almond extract\":43,\n  \"frozen spinach\":6,\n  \"allspice\":4,\n  \"celery seeds\":2,\n  \"dijon mustard\":16,\n  \"soy sauce\":5,\n  \"broccoli\":3,\n  \"apples\":11,\n  \"yellow mustard\":2,\n  \"maple syrup\":4,\n  \"sweet potatoes\":4,\n  \"egg whites\":18,\n  \"worcestershire sauce\":82,\n  \"brown sugar\":76,\n  \"lime juice\":11,\n  \"dry mustard\":19,\n  \"raw shrimp\":2,\n  \"white flour\":3,\n  \"blueberries\":17,\n  \"mayonnaise\":88,\n  \"fresh thyme\":2,\n  \"olive oil\":20,\n  \"vegetable oil\":31,\n  \"curry powder\":16,\n  \"flour\":282,\n  \"vinegar\":10,\n  \"white wine\":3,\n  \"sherry\":5,\n  \"oats\":2,\n  \"whipping cream\":95,\n  \"chicken breast\":2,\n  \"sugar\":831},\n \"dry mustard\":\n {\"elbow macaroni\":15,\n  \"red wine\":6,\n  \"canned tomato sauce\":7,\n  \"bacon\":51,\n  \"cayenne pepper\":75,\n  \"lemon juice\":151,\n  \"honey\":90,\n  \"swiss cheese\":10,\n  \"paprika\":208,\n  \"bread\":18,\n  \"ground cinnamon\":18,\n  \"yeast\":3,\n  \"egg yolks\":25,\n  \"green pepper\":52,\n  \"baking powder\":16,\n  \"saffron\":3,\n  \"sesame oil\":9,\n  \"pine nuts\":2,\n  \"cream of tartar\":2,\n  \"cinnamon\":22,\n  \"avocado\":2,\n  \"cheddar cheese\":56,\n  \"tomatoes\":25,\n  \"white pepper\":57,\n  \"brown rice\":3,\n  \"black beans\":3,\n  \"pinto beans\":4,\n  \"fresh garlic\":4,\n  \"cauliflower\":3,\n  \"frozen peas\":4,\n  \"butter\":171,\n  \"salmon\":2,\n  \"orange juice\":19,\n  \"lemon\":25,\n  \"black pepper\":133,\n  \"tumeric\":13,\n  \"black olives\":2,\n  \"buttermilk\":5,\n  \"dried oregano\":14,\n  \"capers\":10,\n  \"cornmeal\":10,\n  \"cloves\":20,\n  \"oil\":65,\n  \"chicken\":11,\n  \"blue cheese\":5,\n  \"cucumber\":10,\n  \"canola oil\":8,\n  \"dried tarragon\":13,\n  \"celery\":32,\n  \"alfalfa sprouts\":2,\n  \"walnuts\":5,\n  \"nutmeg\":27,\n  \"fresh cilantro\":2,\n  \"bread crumbs\":30,\n  \"baking soda\":7,\n  \"corn syrup\":2,\n  \"tabasco sauce\":51,\n  \"ginger\":50,\n  \"balsamic vinegar\":7,\n  \"fresh rosemary\":2,\n  \"lime\":2,\n  \"pecans\":5,\n  \"peanut butter\":3,\n  \"frozen corn\":3,\n  \"green onions\":36,\n  \"cumin\":32,\n  \"canned chicken broth\":2,\n  \"skim milk\":31,\n  \"beets\":3,\n  \"kidney beans\":6,\n  \"shortening\":12,\n  \"applesauce\":4,\n  \"parmesan cheese\":28,\n  \"carrots\":36,\n  \"mushrooms\":31,\n  \"sour cream\":46,\n  \"yogurt\":4,\n  \"cream cheese\":19,\n  \"cherry tomatoes\":6,\n  \"salsa\":2,\n  \"parsley\":49,\n  \"rice vinegar\":8,\n  \"molasses\":73,\n  \"chili powder\":94,\n  \"beer\":31,\n  \"spaghetti\":3,\n  \"raisins\":11,\n  \"red wine vinegar\":48,\n  \"garam masala\":4,\n  \"chives\":10,\n  \"cornstarch\":48,\n  \"dried basil\":17,\n  \"orange\":5,\n  \"radishes\":2,\n  \"catsup\":67,\n  \"ground beef\":32,\n  \"mozzarella cheese\":3,\n  \"sweet red pepper\":8,\n  \"sweet red onions\":2,\n  \"cumin seeds\":3,\n  \"barbecue sauce\":5,\n  \"red pepper flakes\":5,\n  \"almonds\":2,\n  \"eggs\":159,\n  \"margarine\":31,\n  \"fresh ginger\":3,\n  \"garlic powder\":133,\n  \"dried thyme\":28,\n  \"cream of mushroom soup\":9,\n  \"whole wheat flour\":2,\n  \"salt\":827,\n  \"fresh basil\":4,\n  \"bean sprouts\":3,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":25,\n  \"frozen spinach\":2,\n  \"celery seeds\":17,\n  \"allspice\":22,\n  \"dijon mustard\":28,\n  \"soy sauce\":102,\n  \"broccoli\":7,\n  \"apples\":7,\n  \"sweet potatoes\":3,\n  \"maple syrup\":17,\n  \"egg whites\":11,\n  \"worcestershire sauce\":292,\n  \"brown sugar\":231,\n  \"lime juice\":8,\n  \"raw shrimp\":2,\n  \"white flour\":2,\n  \"mayonnaise\":77,\n  \"chicken wings\":13,\n  \"fresh thyme\":3,\n  \"vegetable oil\":88,\n  \"olive oil\":108,\n  \"curry powder\":31,\n  \"flour\":169,\n  \"vinegar\":195,\n  \"white wine\":15,\n  \"sherry\":20,\n  \"oats\":2,\n  \"whipping cream\":7,\n  \"chicken breast\":4,\n  \"sugar\":254},\n \"potatoes\":\n {\"red wine\":10,\n  \"canned tomato sauce\":2,\n  \"coconut oil\":4,\n  \"bacon\":75,\n  \"canned clams\":2,\n  \"brown onions\":3,\n  \"cayenne pepper\":25,\n  \"honey\":9,\n  \"lemon juice\":65,\n  \"zucchini squash\":2,\n  \"swiss cheese\":5,\n  \"paprika\":126,\n  \"ground cinnamon\":10,\n  \"bread\":10,\n  \"yeast\":5,\n  \"egg yolks\":9,\n  \"green pepper\":56,\n  \"baking powder\":18,\n  \"saffron\":5,\n  \"sesame oil\":2,\n  \"cream of tartar\":2,\n  \"pine nuts\":2,\n  \"cinnamon\":27,\n  \"cheddar cheese\":48,\n  \"tomatoes\":153,\n  \"white pepper\":31,\n  \"brown rice\":4,\n  \"black beans\":4,\n  \"pinto beans\":2,\n  \"cauliflower\":24,\n  \"butter\":325,\n  \"frozen peas\":34,\n  \"orange juice\":10,\n  \"lemon\":17,\n  \"black pepper\":118,\n  \"tumeric\":11,\n  \"black olives\":7,\n  \"buttermilk\":5,\n  \"powdered sugar\":2,\n  \"eggplant\":18,\n  \"dried oregano\":25,\n  \"capers\":4,\n  \"cornmeal\":2,\n  \"cloves\":15,\n  \"oil\":140,\n  \"chicken\":25,\n  \"blue cheese\":5,\n  \"cucumber\":10,\n  \"flour tortilla\":3,\n  \"canola oil\":11,\n  \"dried tarragon\":4,\n  \"celery\":103,\n  \"fresh cilantro\":7,\n  \"nutmeg\":44,\n  \"walnuts\":3,\n  \"baking soda\":5,\n  \"bread crumbs\":26,\n  \"tabasco sauce\":6,\n  \"balsamic vinegar\":2,\n  \"ginger\":17,\n  \"fresh rosemary\":6,\n  \"lime\":4,\n  \"pecans\":6,\n  \"peanut butter\":6,\n  \"cumin\":29,\n  \"frozen corn\":10,\n  \"green onions\":45,\n  \"canned chicken broth\":2,\n  \"banana\":2,\n  \"skim milk\":20,\n  \"beets\":12,\n  \"barley\":12,\n  \"kidney beans\":3,\n  \"shortening\":13,\n  \"applesauce\":4,\n  \"parmesan cheese\":29,\n  \"carrots\":369,\n  \"mushrooms\":49,\n  \"sour cream\":72,\n  \"yogurt\":12,\n  \"brussels sprouts\":3,\n  \"cream cheese\":15,\n  \"tofu\":3,\n  \"cherry tomatoes\":5,\n  \"salsa\":6,\n  \"parsley\":139,\n  \"green olives\":4,\n  \"molasses\":3,\n  \"chili powder\":29,\n  \"beer\":11,\n  \"raisins\":7,\n  \"red wine vinegar\":6,\n  \"garam masala\":32,\n  \"chives\":20,\n  \"cornstarch\":26,\n  \"orange\":2,\n  \"dried basil\":20,\n  \"radishes\":4,\n  \"catsup\":11,\n  \"ground beef\":42,\n  \"adzuki beans\":2,\n  \"mozzarella cheese\":4,\n  \"sweet red pepper\":5,\n  \"cumin seeds\":19,\n  \"barbecue sauce\":3,\n  \"raw spinach\":2,\n  \"red pepper flakes\":4,\n  \"eggs\":138,\n  \"almonds\":5,\n  \"margarine\":66,\n  \"fresh ginger\":15,\n  \"kale\":9,\n  \"garlic powder\":45,\n  \"dried thyme\":32,\n  \"cream of mushroom soup\":21,\n  \"vanilla\":6,\n  \"whole wheat flour\":11,\n  \"salt\":857,\n  \"fresh basil\":5,\n  \"bean sprouts\":5,\n  \"frozen spinach\":2,\n  \"celery seeds\":3,\n  \"allspice\":12,\n  \"dijon mustard\":8,\n  \"soy sauce\":32,\n  \"broccoli\":10,\n  \"apples\":11,\n  \"maple syrup\":2,\n  \"white rice\":2,\n  \"yellow mustard\":4,\n  \"sweet potatoes\":5,\n  \"egg whites\":11,\n  \"worcestershire sauce\":66,\n  \"brown sugar\":18,\n  \"lime juice\":7,\n  \"dry mustard\":25,\n  \"blueberries\":2,\n  \"mayonnaise\":24,\n  \"olive oil\":190,\n  \"vegetable oil\":114,\n  \"fresh thyme\":8,\n  \"curry powder\":28,\n  \"flour\":263,\n  \"vinegar\":52,\n  \"white wine\":12,\n  \"sherry\":7,\n  \"oats\":4,\n  \"whipping cream\":14,\n  \"chicken breast\":3,\n  \"sugar\":154},\n \"lime\":\n {\"red wine\":3,\n  \"bacon\":2,\n  \"cayenne pepper\":12,\n  \"lemon juice\":7,\n  \"honey\":13,\n  \"paprika\":3,\n  \"ground cinnamon\":4,\n  \"green pepper\":4,\n  \"sesame oil\":3,\n  \"pine nuts\":3,\n  \"cinnamon\":12,\n  \"cream of tartar\":2,\n  \"avocado\":5,\n  \"corn tortilla\":3,\n  \"tomatoes\":12,\n  \"white pepper\":4,\n  \"brown rice\":2,\n  \"black beans\":6,\n  \"butter\":16,\n  \"salmon\":2,\n  \"orange juice\":9,\n  \"lemon\":26,\n  \"black pepper\":14,\n  \"powdered sugar\":3,\n  \"dried oregano\":12,\n  \"eggplant\":2,\n  \"hoisin sauce\":3,\n  \"capers\":3,\n  \"cloves\":11,\n  \"oil\":13,\n  \"chicken\":7,\n  \"cucumber\":10,\n  \"canola oil\":2,\n  \"strawberries\":5,\n  \"celery\":6,\n  \"cornish game hens\":2,\n  \"nutmeg\":8,\n  \"fresh cilantro\":8,\n  \"tabasco sauce\":5,\n  \"ginger\":11,\n  \"balsamic vinegar\":4,\n  \"cumin\":17,\n  \"green onions\":11,\n  \"skim milk\":2,\n  \"beets\":2,\n  \"banana\":2,\n  \"barley\":2,\n  \"carrots\":4,\n  \"mushrooms\":2,\n  \"sour cream\":10,\n  \"yogurt\":2,\n  \"cream cheese\":2,\n  \"cherry tomatoes\":3,\n  \"salsa\":4,\n  \"parsley\":7,\n  \"green olives\":3,\n  \"rice vinegar\":2,\n  \"molasses\":2,\n  \"beer\":5,\n  \"chili powder\":8,\n  \"raisins\":7,\n  \"red wine vinegar\":5,\n  \"garam masala\":3,\n  \"chives\":3,\n  \"cornstarch\":5,\n  \"orange\":19,\n  \"radishes\":2,\n  \"sweet red pepper\":2,\n  \"cumin seeds\":3,\n  \"red pepper flakes\":5,\n  \"eggs\":3,\n  \"fresh ginger\":5,\n  \"garlic powder\":4,\n  \"dried thyme\":3,\n  \"vanilla\":3,\n  \"salt\":93,\n  \"bean sprouts\":6,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":4,\n  \"allspice\":6,\n  \"dijon mustard\":3,\n  \"soy sauce\":11,\n  \"sweet potatoes\":3,\n  \"egg whites\":4,\n  \"worcestershire sauce\":3,\n  \"brown sugar\":12,\n  \"lime juice\":10,\n  \"dry mustard\":2,\n  \"blueberries\":2,\n  \"mayonnaise\":5,\n  \"vegetable oil\":24,\n  \"olive oil\":38,\n  \"flour\":6,\n  \"curry powder\":2,\n  \"vinegar\":6,\n  \"white wine\":5,\n  \"sherry\":2,\n  \"whipping cream\":3,\n  \"chicken breast\":4,\n  \"sugar\":51},\n \"salt\":\n {\"canned beef broth\":7,\n  \"elbow macaroni\":59,\n  \"red wine\":165,\n  \"canned tomato sauce\":21,\n  \"coconut oil\":13,\n  \"bacon\":516,\n  \"canned clams\":3,\n  \"brown onions\":3,\n  \"cayenne pepper\":928,\n  \"lemon juice\":2421,\n  \"honey\":1244,\n  \"zucchini squash\":10,\n  \"swiss cheese\":129,\n  \"paprika\":1611,\n  \"camembert cheese\":2,\n  \"bread\":131,\n  \"ground cinnamon\":843,\n  \"dried bay leaf\":2,\n  \"yeast\":1230,\n  \"egg yolks\":718,\n  \"green pepper\":767,\n  \"baking powder\":4656,\n  \"saffron\":83,\n  \"sesame oil\":391,\n  \"pine nuts\":167,\n  \"cinnamon\":2385,\n  \"cream of tartar\":344,\n  \"avocado\":71,\n  \"cheddar cheese\":392,\n  \"corn tortilla\":8,\n  \"tomatoes\":1322,\n  \"white pepper\":722,\n  \"brown rice\":75,\n  \"black beans\":114,\n  \"fresh garlic\":19,\n  \"pinto beans\":64,\n  \"cauliflower\":89,\n  \"butter\":6434,\n  \"frozen peas\":127,\n  \"salmon\":40,\n  \"orange juice\":548,\n  \"lemon\":483,\n  \"black pepper\":1964,\n  \"tumeric\":58,\n  \"black olives\":73,\n  \"buttermilk\":829,\n  \"powdered sugar\":468,\n  \"hoisin sauce\":51,\n  \"dried oregano\":472,\n  \"eggplant\":277,\n  \"capers\":151,\n  \"cornmeal\":438,\n  \"cloves\":611,\n  \"oil\":2800,\n  \"chicken\":414,\n  \"blue cheese\":38,\n  \"cucumber\":223,\n  \"dry rosemary\":2,\n  \"flour tortilla\":7,\n  \"canola oil\":263,\n  \"dried tarragon\":72,\n  \"strawberries\":78,\n  \"celery\":797,\n  \"cornish game hens\":18,\n  \"alfalfa sprouts\":13,\n  \"fresh cilantro\":144,\n  \"fresh peaches\":21,\n  \"nutmeg\":1340,\n  \"walnuts\":521,\n  \"bread crumbs\":415,\n  \"baking soda\":3399,\n  \"corn syrup\":133,\n  \"tabasco sauce\":272,\n  \"ginger\":677,\n  \"balsamic vinegar\":214,\n  \"fresh rosemary\":50,\n  \"lime\":93,\n  \"pecans\":374,\n  \"peanut butter\":229,\n  \"frozen corn\":42,\n  \"cumin\":625,\n  \"green onions\":622,\n  \"canned chicken broth\":19,\n  \"skim milk\":422,\n  \"banana\":72,\n  \"beets\":53,\n  \"barley\":42,\n  \"kidney beans\":104,\n  \"shortening\":1377,\n  \"parmesan cheese\":640,\n  \"applesauce\":280,\n  \"carrots\":1057,\n  \"cashew\":2,\n  \"mushrooms\":672,\n  \"sour cream\":1198,\n  \"yogurt\":150,\n  \"brussels sprouts\":11,\n  \"tofu\":57,\n  \"cream cheese\":399,\n  \"cherry tomatoes\":64,\n  \"salsa\":65,\n  \"parsley\":1139,\n  \"green olives\":39,\n  \"rice vinegar\":112,\n  \"molasses\":609,\n  \"chili powder\":1111,\n  \"beer\":166,\n  \"spaghetti\":94,\n  \"raisins\":1175,\n  \"red wine vinegar\":432,\n  \"garam masala\":143,\n  \"chives\":167,\n  \"cornstarch\":1741,\n  \"dried basil\":353,\n  \"orange\":122,\n  \"catsup\":287,\n  \"radishes\":42,\n  \"ground beef\":712,\n  \"adzuki beans\":3,\n  \"romaine lettuce\":11,\n  \"mozzarella cheese\":209,\n  \"sweet red pepper\":89,\n  \"canned tuna\":2,\n  \"cumin seeds\":193,\n  \"canned peaches\":3,\n  \"barbecue sauce\":32,\n  \"raw spinach\":3,\n  \"red pepper flakes\":149,\n  \"eggs\":6373,\n  \"almonds\":240,\n  \"margarine\":1259,\n  \"fresh ginger\":144,\n  \"kale\":20,\n  \"garlic powder\":935,\n  \"dried thyme\":485,\n  \"cream of mushroom soup\":93,\n  \"low fat milk\":10,\n  \"vanilla\":3155,\n  \"whole wheat flour\":756,\n  \"fresh basil\":141,\n  \"bean sprouts\":83,\n  \"shiitake mushrooms\":31,\n  \"potatoes\":857,\n  \"almond extract\":330,\n  \"frozen spinach\":38,\n  \"canned apricots\":2,\n  \"celery seeds\":57,\n  \"allspice\":443,\n  \"dijon mustard\":346,\n  \"soy sauce\":1292,\n  \"broccoli\":111,\n  \"chicken legs\":20,\n  \"apples\":303,\n  \"shell macaroni\":2,\n  \"white rice\":34,\n  \"yellow mustard\":28,\n  \"maple syrup\":182,\n  \"sweet potatoes\":132,\n  \"canned tomato paste\":3,\n  \"red leaf lettuce\":2,\n  \"egg whites\":945,\n  \"worcestershire sauce\":1000,\n  \"dried cilantro\":6,\n  \"brown sugar\":2255,\n  \"lime juice\":348,\n  \"dry mustard\":827,\n  \"raw shrimp\":28,\n  \"canned vegetable broth\":2,\n  \"white flour\":164,\n  \"blueberries\":113,\n  \"mayonnaise\":493,\n  \"chicken wings\":57,\n  \"vegetable oil\":2524,\n  \"olive oil\":3604,\n  \"fresh thyme\":81,\n  \"curry powder\":474,\n  \"flour\":7567,\n  \"vinegar\":1359,\n  \"lamb shank\":2,\n  \"white wine\":342,\n  \"sherry\":592,\n  \"yam\":2,\n  \"oats\":163,\n  \"whipping cream\":450,\n  \"chicken breast\":125,\n  \"sugar\":12133},\n \"sugar\":\n {\"elbow macaroni\":6,\n  \"red wine\":58,\n  \"canned tomato sauce\":3,\n  \"coconut oil\":2,\n  \"bacon\":128,\n  \"canned clams\":2,\n  \"brown onions\":2,\n  \"cayenne pepper\":136,\n  \"lemon juice\":1688,\n  \"honey\":409,\n  \"swiss cheese\":25,\n  \"paprika\":255,\n  \"bread\":46,\n  \"ground cinnamon\":843,\n  \"yeast\":874,\n  \"egg yolks\":1122,\n  \"green pepper\":202,\n  \"baking powder\":3957,\n  \"saffron\":17,\n  \"sesame oil\":391,\n  \"pine nuts\":58,\n  \"cinnamon\":2551,\n  \"cream of tartar\":479,\n  \"avocado\":16,\n  \"cheddar cheese\":67,\n  \"tomatoes\":272,\n  \"white pepper\":125,\n  \"brown rice\":6,\n  \"black beans\":14,\n  \"pinto beans\":8,\n  \"fresh garlic\":9,\n  \"cauliflower\":26,\n  \"frozen peas\":14,\n  \"butter\":4965,\n  \"salmon\":8,\n  \"orange juice\":633,\n  \"black pepper\":297,\n  \"lemon\":379,\n  \"tumeric\":25,\n  \"black olives\":14,\n  \"buttermilk\":653,\n  \"powdered sugar\":496,\n  \"hoisin sauce\":69,\n  \"dried oregano\":98,\n  \"eggplant\":56,\n  \"capers\":41,\n  \"cornmeal\":195,\n  \"cloves\":418,\n  \"oil\":1465,\n  \"chicken\":69,\n  \"blue cheese\":12,\n  \"cucumber\":106,\n  \"dry rosemary\":2,\n  \"canola oil\":100,\n  \"dried tarragon\":13,\n  \"strawberries\":269,\n  \"celery\":199,\n  \"alfalfa sprouts\":4,\n  \"cornish game hens\":5,\n  \"fresh cilantro\":25,\n  \"nutmeg\":955,\n  \"walnuts\":527,\n  \"fresh peaches\":34,\n  \"bread crumbs\":42,\n  \"baking soda\":2741,\n  \"tabasco sauce\":84,\n  \"corn syrup\":183,\n  \"balsamic vinegar\":71,\n  \"ginger\":487,\n  \"fresh rosemary\":8,\n  \"lime\":51,\n  \"pecans\":474,\n  \"peanut butter\":258,\n  \"frozen corn\":5,\n  \"green onions\":253,\n  \"cumin\":93,\n  \"canned chicken broth\":5,\n  \"beets\":46,\n  \"skim milk\":282,\n  \"banana\":72,\n  \"barley\":5,\n  \"kidney beans\":25,\n  \"shortening\":1042,\n  \"parmesan cheese\":99,\n  \"applesauce\":194,\n  \"carrots\":316,\n  \"mushrooms\":96,\n  \"sour cream\":809,\n  \"yogurt\":69,\n  \"brussels sprouts\":2,\n  \"cream cheese\":831,\n  \"tofu\":32,\n  \"cherry tomatoes\":21,\n  \"salsa\":7,\n  \"parsley\":163,\n  \"green olives\":15,\n  \"rice vinegar\":121,\n  \"molasses\":256,\n  \"chili powder\":197,\n  \"beer\":68,\n  \"spaghetti\":18,\n  \"raisins\":982,\n  \"red wine vinegar\":165,\n  \"chives\":22,\n  \"garam masala\":18,\n  \"cornstarch\":1780,\n  \"dried basil\":80,\n  \"orange\":208,\n  \"catsup\":96,\n  \"radishes\":18,\n  \"ground beef\":102,\n  \"adzuki beans\":2,\n  \"mozzarella cheese\":40,\n  \"romaine lettuce\":6,\n  \"sweet red pepper\":23,\n  \"cumin seeds\":25,\n  \"canned peaches\":4,\n  \"barbecue sauce\":10,\n  \"red pepper flakes\":45,\n  \"eggs\":5984,\n  \"almonds\":323,\n  \"margarine\":1005,\n  \"fresh ginger\":79,\n  \"kale\":5,\n  \"garlic powder\":192,\n  \"dried thyme\":67,\n  \"cream of mushroom soup\":3,\n  \"low fat milk\":3,\n  \"vanilla\":4410,\n  \"whole wheat flour\":302,\n  \"salt\":12133,\n  \"fresh basil\":31,\n  \"bean sprouts\":74,\n  \"shiitake mushrooms\":7,\n  \"potatoes\":154,\n  \"almond extract\":510,\n  \"frozen spinach\":8,\n  \"celery seeds\":36,\n  \"allspice\":264,\n  \"dijon mustard\":83,\n  \"soy sauce\":1198,\n  \"broccoli\":30,\n  \"chicken legs\":6,\n  \"apples\":384,\n  \"yellow mustard\":6,\n  \"sweet potatoes\":56,\n  \"maple syrup\":74,\n  \"white rice\":8,\n  \"red leaf lettuce\":2,\n  \"egg whites\":1246,\n  \"worcestershire sauce\":197,\n  \"dried cilantro\":4,\n  \"brown sugar\":782,\n  \"lime juice\":204,\n  \"dry mustard\":254,\n  \"raw shrimp\":12,\n  \"white flour\":93,\n  \"blueberries\":212,\n  \"mayonnaise\":159,\n  \"chicken wings\":23,\n  \"fresh thyme\":10,\n  \"olive oil\":673,\n  \"vegetable oil\":1133,\n  \"flour\":5965,\n  \"curry powder\":83,\n  \"vinegar\":1030,\n  \"white wine\":87,\n  \"sherry\":401,\n  \"oats\":90,\n  \"whipping cream\":750,\n  \"chicken breast\":61},\n \"canned tomato sauce\":\n {\"fresh basil\":2,\n  \"sour cream\":3,\n  \"elbow macaroni\":3,\n  \"tofu\":3,\n  \"potatoes\":2,\n  \"dried oregano\":5,\n  \"capers\":2,\n  \"parsley\":2,\n  \"lemon juice\":2,\n  \"honey\":2,\n  \"oil\":2,\n  \"chicken\":2,\n  \"molasses\":3,\n  \"paprika\":4,\n  \"chili powder\":5,\n  \"allspice\":2,\n  \"spaghetti\":4,\n  \"ground cinnamon\":3,\n  \"raisins\":4,\n  \"celery\":2,\n  \"green pepper\":2,\n  \"nutmeg\":4,\n  \"worcestershire sauce\":2,\n  \"dried basil\":2,\n  \"catsup\":4,\n  \"tabasco sauce\":4,\n  \"brown sugar\":4,\n  \"cinnamon\":2,\n  \"pine nuts\":2,\n  \"ground beef\":2,\n  \"dry mustard\":7,\n  \"fresh rosemary\":2,\n  \"cheddar cheese\":3,\n  \"tomatoes\":2,\n  \"cumin\":5,\n  \"white pepper\":4,\n  \"vegetable oil\":11,\n  \"olive oil\":10,\n  \"brown rice\":2,\n  \"eggs\":2,\n  \"margarine\":2,\n  \"vinegar\":2,\n  \"butter\":3,\n  \"garlic powder\":7,\n  \"dried thyme\":2,\n  \"oats\":2,\n  \"low fat milk\":2,\n  \"sugar\":3,\n  \"black pepper\":6,\n  \"salt\":21,\n  \"mushrooms\":4},\n \"pine nuts\":\n {\"red wine\":3,\n  \"canned tomato sauce\":2,\n  \"cayenne pepper\":5,\n  \"lemon juice\":24,\n  \"honey\":14,\n  \"zucchini squash\":2,\n  \"paprika\":2,\n  \"bread\":2,\n  \"ground cinnamon\":16,\n  \"yeast\":2,\n  \"egg yolks\":4,\n  \"green pepper\":7,\n  \"baking powder\":21,\n  \"saffron\":4,\n  \"sesame oil\":2,\n  \"cinnamon\":21,\n  \"tomatoes\":16,\n  \"white pepper\":6,\n  \"brown rice\":3,\n  \"fresh garlic\":2,\n  \"cauliflower\":2,\n  \"butter\":57,\n  \"frozen peas\":2,\n  \"orange juice\":3,\n  \"black pepper\":24,\n  \"lemon\":8,\n  \"black olives\":12,\n  \"buttermilk\":3,\n  \"powdered sugar\":4,\n  \"eggplant\":10,\n  \"hoisin sauce\":5,\n  \"dried oregano\":8,\n  \"capers\":10,\n  \"cornmeal\":5,\n  \"cloves\":5,\n  \"oil\":15,\n  \"chicken\":5,\n  \"cucumber\":2,\n  \"canola oil\":4,\n  \"dried tarragon\":3,\n  \"strawberries\":3,\n  \"celery\":4,\n  \"nutmeg\":11,\n  \"walnuts\":8,\n  \"fresh cilantro\":3,\n  \"baking soda\":10,\n  \"bread crumbs\":13,\n  \"tabasco sauce\":2,\n  \"ginger\":4,\n  \"balsamic vinegar\":11,\n  \"fresh rosemary\":2,\n  \"lime\":3,\n  \"pecans\":2,\n  \"cumin\":2,\n  \"green onions\":8,\n  \"canned chicken broth\":2,\n  \"banana\":2,\n  \"skim milk\":4,\n  \"shortening\":3,\n  \"parmesan cheese\":43,\n  \"carrots\":10,\n  \"mushrooms\":11,\n  \"sour cream\":4,\n  \"yogurt\":5,\n  \"cream cheese\":8,\n  \"cherry tomatoes\":6,\n  \"parsley\":30,\n  \"green olives\":4,\n  \"rice vinegar\":2,\n  \"chili powder\":4,\n  \"spaghetti\":4,\n  \"raisins\":29,\n  \"red wine vinegar\":14,\n  \"chives\":8,\n  \"cornstarch\":11,\n  \"dried basil\":10,\n  \"orange\":5,\n  \"ground beef\":6,\n  \"mozzarella cheese\":4,\n  \"cumin seeds\":3,\n  \"red pepper flakes\":4,\n  \"almonds\":12,\n  \"eggs\":33,\n  \"margarine\":4,\n  \"fresh ginger\":4,\n  \"garlic powder\":3,\n  \"dried thyme\":6,\n  \"vanilla\":8,\n  \"whole wheat flour\":4,\n  \"salt\":167,\n  \"fresh basil\":15,\n  \"potatoes\":2,\n  \"almond extract\":5,\n  \"frozen spinach\":2,\n  \"allspice\":12,\n  \"celery seeds\":2,\n  \"dijon mustard\":7,\n  \"soy sauce\":8,\n  \"broccoli\":4,\n  \"maple syrup\":2,\n  \"white rice\":2,\n  \"egg whites\":11,\n  \"worcestershire sauce\":4,\n  \"brown sugar\":2,\n  \"lime juice\":4,\n  \"dry mustard\":2,\n  \"mayonnaise\":5,\n  \"vegetable oil\":17,\n  \"olive oil\":175,\n  \"fresh thyme\":2,\n  \"curry powder\":3,\n  \"flour\":23,\n  \"vinegar\":2,\n  \"white wine\":6,\n  \"whipping cream\":3,\n  \"sugar\":58},\n \"yeast\":\n {\"bacon\":3,\n  \"cayenne pepper\":5,\n  \"lemon juice\":11,\n  \"honey\":247,\n  \"swiss cheese\":7,\n  \"paprika\":7,\n  \"ground cinnamon\":9,\n  \"egg yolks\":11,\n  \"baking powder\":21,\n  \"saffron\":3,\n  \"sesame oil\":2,\n  \"pine nuts\":2,\n  \"cinnamon\":121,\n  \"cream of tartar\":4,\n  \"cheddar cheese\":14,\n  \"white pepper\":2,\n  \"fresh garlic\":2,\n  \"butter\":326,\n  \"orange juice\":12,\n  \"black pepper\":17,\n  \"lemon\":10,\n  \"black olives\":3,\n  \"buttermilk\":50,\n  \"powdered sugar\":15,\n  \"hoisin sauce\":2,\n  \"dried oregano\":8,\n  \"eggplant\":2,\n  \"capers\":2,\n  \"cornmeal\":52,\n  \"cloves\":11,\n  \"oil\":141,\n  \"chicken\":2,\n  \"blue cheese\":5,\n  \"cucumber\":2,\n  \"canola oil\":25,\n  \"strawberries\":4,\n  \"celery\":7,\n  \"alfalfa sprouts\":2,\n  \"fresh cilantro\":2,\n  \"fresh peaches\":2,\n  \"nutmeg\":46,\n  \"walnuts\":13,\n  \"baking soda\":58,\n  \"bread crumbs\":2,\n  \"tabasco sauce\":3,\n  \"corn syrup\":4,\n  \"ginger\":30,\n  \"fresh rosemary\":2,\n  \"pecans\":17,\n  \"peanut butter\":3,\n  \"cumin\":2,\n  \"skim milk\":10,\n  \"banana\":14,\n  \"shortening\":59,\n  \"parmesan cheese\":30,\n  \"applesauce\":89,\n  \"carrots\":5,\n  \"mushrooms\":8,\n  \"sour cream\":19,\n  \"yogurt\":11,\n  \"cream cheese\":4,\n  \"salsa\":2,\n  \"parsley\":18,\n  \"molasses\":99,\n  \"beer\":10,\n  \"chili powder\":3,\n  \"raisins\":91,\n  \"chives\":6,\n  \"cornstarch\":4,\n  \"orange\":4,\n  \"dried basil\":10,\n  \"mozzarella cheese\":8,\n  \"barbecue sauce\":4,\n  \"eggs\":166,\n  \"almonds\":20,\n  \"margarine\":77,\n  \"fresh ginger\":2,\n  \"garlic powder\":19,\n  \"dried thyme\":3,\n  \"cream of mushroom soup\":2,\n  \"vanilla\":32,\n  \"whole wheat flour\":221,\n  \"salt\":1230,\n  \"fresh basil\":2,\n  \"bean sprouts\":2,\n  \"potatoes\":5,\n  \"almond extract\":7,\n  \"celery seeds\":3,\n  \"allspice\":13,\n  \"dijon mustard\":6,\n  \"soy sauce\":4,\n  \"apples\":8,\n  \"sweet potatoes\":3,\n  \"yellow mustard\":2,\n  \"maple syrup\":22,\n  \"egg whites\":19,\n  \"worcestershire sauce\":4,\n  \"brown sugar\":100,\n  \"lime juice\":2,\n  \"dry mustard\":3,\n  \"white flour\":51,\n  \"blueberries\":5,\n  \"vegetable oil\":92,\n  \"olive oil\":145,\n  \"flour\":410,\n  \"curry powder\":4,\n  \"vinegar\":9,\n  \"white wine\":5,\n  \"oats\":43,\n  \"whipping cream\":3,\n  \"chicken breast\":3,\n  \"sugar\":874},\n \"shiitake mushrooms\":\n {\"salsa\":2,\n  \"cayenne pepper\":3,\n  \"parsley\":4,\n  \"lemon juice\":8,\n  \"honey\":5,\n  \"rice vinegar\":4,\n  \"spaghetti\":2,\n  \"red wine vinegar\":2,\n  \"egg yolks\":3,\n  \"cornstarch\":7,\n  \"dried basil\":4,\n  \"orange\":3,\n  \"sesame oil\":13,\n  \"cinnamon\":2,\n  \"mozzarella cheese\":2,\n  \"tomatoes\":2,\n  \"white pepper\":2,\n  \"red pepper flakes\":3,\n  \"eggs\":6,\n  \"fresh ginger\":3,\n  \"frozen peas\":2,\n  \"butter\":13,\n  \"kale\":2,\n  \"dried thyme\":4,\n  \"lemon\":3,\n  \"black pepper\":7,\n  \"salt\":31,\n  \"fresh basil\":2,\n  \"bean sprouts\":4,\n  \"buttermilk\":2,\n  \"dried oregano\":2,\n  \"hoisin sauce\":2,\n  \"eggplant\":2,\n  \"cornmeal\":2,\n  \"oil\":4,\n  \"cucumber\":2,\n  \"allspice\":2,\n  \"canola oil\":4,\n  \"dijon mustard\":2,\n  \"soy sauce\":24,\n  \"celery\":5,\n  \"sweet potatoes\":2,\n  \"nutmeg\":2,\n  \"egg whites\":2,\n  \"worcestershire sauce\":2,\n  \"brown sugar\":2,\n  \"balsamic vinegar\":3,\n  \"dry mustard\":2,\n  \"fresh rosemary\":2,\n  \"lime\":2,\n  \"canned vegetable broth\":2,\n  \"pecans\":2,\n  \"green onions\":9,\n  \"olive oil\":21,\n  \"vegetable oil\":8,\n  \"fresh thyme\":3,\n  \"skim milk\":2,\n  \"beets\":2,\n  \"flour\":3,\n  \"curry powder\":3,\n  \"white wine\":5,\n  \"parmesan cheese\":6,\n  \"carrots\":8,\n  \"sherry\":4,\n  \"chicken breast\":3,\n  \"sugar\":7,\n  \"mushrooms\":4},\n \"dried thyme\":\n {\"elbow macaroni\":2,\n  \"red wine\":13,\n  \"canned beef broth\":3,\n  \"canned tomato sauce\":2,\n  \"bacon\":20,\n  \"coconut oil\":2,\n  \"cayenne pepper\":69,\n  \"honey\":26,\n  \"lemon juice\":56,\n  \"swiss cheese\":2,\n  \"paprika\":92,\n  \"camembert cheese\":2,\n  \"bread\":3,\n  \"ground cinnamon\":19,\n  \"yeast\":3,\n  \"egg yolks\":4,\n  \"green pepper\":15,\n  \"baking powder\":16,\n  \"saffron\":3,\n  \"sesame oil\":2,\n  \"cream of tartar\":2,\n  \"pine nuts\":6,\n  \"cinnamon\":10,\n  \"cheddar cheese\":4,\n  \"tomatoes\":48,\n  \"white pepper\":38,\n  \"brown rice\":6,\n  \"black beans\":4,\n  \"pinto beans\":3,\n  \"fresh garlic\":4,\n  \"cauliflower\":3,\n  \"butter\":111,\n  \"frozen peas\":10,\n  \"salmon\":2,\n  \"orange juice\":15,\n  \"lemon\":15,\n  \"black pepper\":112,\n  \"black olives\":2,\n  \"buttermilk\":6,\n  \"dried oregano\":140,\n  \"eggplant\":7,\n  \"capers\":6,\n  \"cornmeal\":11,\n  \"cloves\":10,\n  \"oil\":22,\n  \"chicken\":14,\n  \"cucumber\":2,\n  \"flour tortilla\":2,\n  \"canola oil\":13,\n  \"dried tarragon\":22,\n  \"celery\":39,\n  \"fresh cilantro\":4,\n  \"walnuts\":6,\n  \"nutmeg\":14,\n  \"bread crumbs\":7,\n  \"baking soda\":7,\n  \"tabasco sauce\":13,\n  \"balsamic vinegar\":24,\n  \"fresh rosemary\":6,\n  \"lime\":3,\n  \"pecans\":5,\n  \"green onions\":26,\n  \"cumin\":7,\n  \"frozen corn\":4,\n  \"canned chicken broth\":4,\n  \"beets\":2,\n  \"skim milk\":5,\n  \"barley\":4,\n  \"kidney beans\":3,\n  \"parmesan cheese\":22,\n  \"carrots\":85,\n  \"mushrooms\":42,\n  \"sour cream\":6,\n  \"yogurt\":2,\n  \"brussels sprouts\":3,\n  \"cream cheese\":3,\n  \"cherry tomatoes\":6,\n  \"salsa\":2,\n  \"parsley\":38,\n  \"rice vinegar\":2,\n  \"molasses\":4,\n  \"beer\":7,\n  \"chili powder\":30,\n  \"spaghetti\":2,\n  \"raisins\":5,\n  \"red wine vinegar\":24,\n  \"chives\":8,\n  \"cornstarch\":34,\n  \"orange\":5,\n  \"dried basil\":126,\n  \"catsup\":5,\n  \"ground beef\":8,\n  \"mozzarella cheese\":6,\n  \"sweet red pepper\":5,\n  \"cumin seeds\":2,\n  \"red pepper flakes\":17,\n  \"almonds\":3,\n  \"eggs\":44,\n  \"margarine\":21,\n  \"fresh ginger\":3,\n  \"kale\":3,\n  \"garlic powder\":69,\n  \"cream of mushroom soup\":2,\n  \"low fat milk\":2,\n  \"whole wheat flour\":6,\n  \"salt\":485,\n  \"fresh basil\":9,\n  \"shiitake mushrooms\":4,\n  \"potatoes\":32,\n  \"frozen spinach\":3,\n  \"celery seeds\":5,\n  \"allspice\":15,\n  \"dijon mustard\":41,\n  \"soy sauce\":23,\n  \"broccoli\":4,\n  \"apples\":5,\n  \"chicken legs\":5,\n  \"sweet potatoes\":4,\n  \"white rice\":5,\n  \"yellow mustard\":2,\n  \"egg whites\":7,\n  \"worcestershire sauce\":39,\n  \"brown sugar\":22,\n  \"lime juice\":6,\n  \"dry mustard\":28,\n  \"raw shrimp\":2,\n  \"mayonnaise\":8,\n  \"chicken wings\":3,\n  \"fresh thyme\":16,\n  \"olive oil\":234,\n  \"vegetable oil\":95,\n  \"flour\":72,\n  \"curry powder\":19,\n  \"vinegar\":7,\n  \"white wine\":14,\n  \"sherry\":10,\n  \"whipping cream\":12,\n  \"chicken breast\":2,\n  \"sugar\":67},\n \"water\":\n {\"elbow macaroni\":22,\n  \"canned beef broth\":5,\n  \"red wine\":73,\n  \"canned tomato sauce\":14,\n  \"bacon\":177,\n  \"cayenne pepper\":269,\n  \"lemon juice\":1111,\n  \"honey\":653,\n  \"zucchini squash\":4,\n  \"swiss cheese\":31,\n  \"paprika\":441,\n  \"bread\":22,\n  \"ground cinnamon\":324,\n  \"yeast\":719,\n  \"egg yolks\":252,\n  \"green pepper\":332,\n  \"baking powder\":566,\n  \"saffron\":27,\n  \"sesame oil\":219,\n  \"cream of tartar\":136,\n  \"cinnamon\":666,\n  \"pine nuts\":47,\n  \"avocado\":20,\n  \"cheddar cheese\":114,\n  \"corn tortilla\":2,\n  \"tomatoes\":460,\n  \"white pepper\":172,\n  \"brown rice\":86,\n  \"black beans\":66,\n  \"fresh garlic\":8,\n  \"pinto beans\":48,\n  \"cauliflower\":34,\n  \"frozen peas\":66,\n  \"butter\":1763,\n  \"salmon\":11,\n  \"orange juice\":234,\n  \"black pepper\":555,\n  \"lemon\":250,\n  \"tumeric\":27,\n  \"black olives\":12,\n  \"buttermilk\":93,\n  \"powdered sugar\":148,\n  \"dried oregano\":175,\n  \"hoisin sauce\":37,\n  \"eggplant\":75,\n  \"capers\":36,\n  \"cornmeal\":152,\n  \"cloves\":229,\n  \"oil\":1314,\n  \"chicken\":151,\n  \"blue cheese\":13,\n  \"cucumber\":62,\n  \"dry rosemary\":2,\n  \"flour tortilla\":2,\n  \"canola oil\":85,\n  \"dried tarragon\":20,\n  \"strawberries\":82,\n  \"celery\":410,\n  \"cornish game hens\":3,\n  \"alfalfa sprouts\":3,\n  \"fresh peaches\":4,\n  \"fresh cilantro\":54,\n  \"nutmeg\":340,\n  \"walnuts\":135,\n  \"baking soda\":552,\n  \"bread crumbs\":118,\n  \"corn syrup\":72,\n  \"tabasco sauce\":111,\n  \"balsamic vinegar\":75,\n  \"ginger\":266,\n  \"fresh rosemary\":14,\n  \"lime\":41,\n  \"pecans\":106,\n  \"peanut butter\":76,\n  \"frozen corn\":23,\n  \"green onions\":290,\n  \"cumin\":240,\n  \"canned chicken broth\":5,\n  \"beets\":30,\n  \"banana\":33,\n  \"skim milk\":102,\n  \"barley\":57,\n  \"kidney beans\":53,\n  \"shortening\":330,\n  \"parmesan cheese\":198,\n  \"applesauce\":122,\n  \"carrots\":628,\n  \"mushrooms\":279,\n  \"sour cream\":297,\n  \"yogurt\":52,\n  \"brussels sprouts\":8,\n  \"tofu\":41,\n  \"cream cheese\":96,\n  \"frozen french fries\":2,\n  \"cherry tomatoes\":20,\n  \"salsa\":37,\n  \"parsley\":409,\n  \"green olives\":10,\n  \"rice vinegar\":65,\n  \"molasses\":240,\n  \"chili powder\":523,\n  \"beer\":38,\n  \"spaghetti\":31,\n  \"raisins\":404,\n  \"red wine vinegar\":146,\n  \"chives\":39,\n  \"garam masala\":57,\n  \"cornstarch\":1444,\n  \"dried basil\":132,\n  \"orange\":73,\n  \"radishes\":17,\n  \"catsup\":163,\n  \"ground beef\":325,\n  \"adzuki beans\":2,\n  \"romaine lettuce\":6,\n  \"mozzarella cheese\":60,\n  \"sweet red pepper\":23,\n  \"sweet red onions\":2,\n  \"canned tuna\":2,\n  \"cumin seeds\":81,\n  \"barbecue sauce\":19,\n  \"red pepper flakes\":75,\n  \"almonds\":92,\n  \"eggs\":1263,\n  \"margarine\":449,\n  \"fresh ginger\":72,\n  \"kale\":14,\n  \"garlic powder\":372,\n  \"dried thyme\":176,\n  \"cream of mushroom soup\":64,\n  \"low fat milk\":2,\n  \"vanilla\":723,\n  \"whole wheat flour\":287,\n  \"salt\":7651,\n  \"fresh basil\":41,\n  \"bean sprouts\":49,\n  \"shiitake mushrooms\":20,\n  \"potatoes\":387,\n  \"almond extract\":101,\n  \"frozen spinach\":8,\n  \"canned apricots\":2,\n  \"celery seeds\":20,\n  \"allspice\":158,\n  \"dijon mustard\":106,\n  \"soy sauce\":1125,\n  \"broccoli\":65,\n  \"chicken legs\":9,\n  \"apples\":132,\n  \"shell macaroni\":4,\n  \"white rice\":23,\n  \"yellow mustard\":9,\n  \"maple syrup\":95,\n  \"sweet potatoes\":51,\n  \"canned tomato paste\":2,\n  \"red leaf lettuce\":2,\n  \"egg whites\":339,\n  \"worcestershire sauce\":438,\n  \"dried cilantro\":4,\n  \"brown sugar\":833,\n  \"lime juice\":106,\n  \"dry mustard\":234,\n  \"raw shrimp\":10,\n  \"canned vegetable broth\":3,\n  \"white flour\":62,\n  \"blueberries\":51,\n  \"mayonnaise\":82,\n  \"chicken wings\":27,\n  \"fresh thyme\":23,\n  \"vegetable oil\":974,\n  \"olive oil\":1119,\n  \"flour\":1951,\n  \"curry powder\":188,\n  \"vinegar\":699,\n  \"lamb shank\":2,\n  \"white wine\":123,\n  \"sherry\":351,\n  \"yam\":3,\n  \"oats\":57,\n  \"whipping cream\":199,\n  \"chicken breast\":60,\n  \"sugar\":4936},\n \"canned tuna\":\n {\"worcestershire sauce\":2,\n  \"cheddar cheese\":2,\n  \"capers\":2,\n  \"mayonnaise\":3,\n  \"lemon juice\":3,\n  \"vegetable oil\":2,\n  \"olive oil\":2,\n  \"eggs\":2,\n  \"bread\":2,\n  \"celery\":2,\n  \"egg yolks\":2,\n  \"vanilla\":2,\n  \"salt\":2,\n  \"black pepper\":2},\n \"radishes\":\n {\"sour cream\":11,\n  \"yogurt\":4,\n  \"red wine\":2,\n  \"cream cheese\":5,\n  \"cherry tomatoes\":8,\n  \"bacon\":2,\n  \"salsa\":2,\n  \"parsley\":5,\n  \"lemon juice\":20,\n  \"rice vinegar\":4,\n  \"swiss cheese\":2,\n  \"paprika\":3,\n  \"spaghetti\":2,\n  \"bread\":2,\n  \"raisins\":2,\n  \"red wine vinegar\":4,\n  \"chives\":2,\n  \"green pepper\":12,\n  \"cornstarch\":5,\n  \"orange\":2,\n  \"sesame oil\":6,\n  \"cinnamon\":2,\n  \"avocado\":3,\n  \"cheddar cheese\":2,\n  \"romaine lettuce\":2,\n  \"mozzarella cheese\":2,\n  \"tomatoes\":7,\n  \"barbecue sauce\":2,\n  \"white pepper\":3,\n  \"eggs\":2,\n  \"margarine\":3,\n  \"fresh ginger\":3,\n  \"cauliflower\":2,\n  \"frozen peas\":2,\n  \"butter\":6,\n  \"orange juice\":3,\n  \"lemon\":2,\n  \"black pepper\":2,\n  \"salt\":42,\n  \"tumeric\":2,\n  \"bean sprouts\":4,\n  \"black olives\":2,\n  \"potatoes\":4,\n  \"dried oregano\":2,\n  \"oil\":8,\n  \"chicken\":4,\n  \"cucumber\":22,\n  \"dijon mustard\":4,\n  \"soy sauce\":10,\n  \"celery\":10,\n  \"broccoli\":2,\n  \"apples\":3,\n  \"alfalfa sprouts\":3,\n  \"walnuts\":2,\n  \"fresh cilantro\":3,\n  \"bread crumbs\":2,\n  \"brown sugar\":2,\n  \"ginger\":4,\n  \"balsamic vinegar\":3,\n  \"lime juice\":3,\n  \"dry mustard\":2,\n  \"lime\":2,\n  \"peanut butter\":2,\n  \"mayonnaise\":9,\n  \"green onions\":13,\n  \"cumin\":2,\n  \"fresh thyme\":2,\n  \"olive oil\":13,\n  \"vegetable oil\":5,\n  \"flour\":2,\n  \"curry powder\":2,\n  \"kidney beans\":2,\n  \"vinegar\":8,\n  \"carrots\":19,\n  \"sherry\":3,\n  \"whipping cream\":2,\n  \"chicken breast\":2,\n  \"sugar\":18,\n  \"mushrooms\":4},\n \"dried oregano\":\n {\"elbow macaroni\":2,\n  \"red wine\":12,\n  \"canned tomato sauce\":5,\n  \"bacon\":9,\n  \"cayenne pepper\":69,\n  \"lemon juice\":70,\n  \"honey\":13,\n  \"swiss cheese\":6,\n  \"paprika\":66,\n  \"camembert cheese\":2,\n  \"bread\":3,\n  \"ground cinnamon\":20,\n  \"dried bay leaf\":2,\n  \"yeast\":8,\n  \"egg yolks\":4,\n  \"green pepper\":40,\n  \"baking powder\":11,\n  \"pine nuts\":8,\n  \"cinnamon\":11,\n  \"cream of tartar\":2,\n  \"avocado\":12,\n  \"cheddar cheese\":14,\n  \"tomatoes\":90,\n  \"white pepper\":13,\n  \"brown rice\":6,\n  \"black beans\":8,\n  \"pinto beans\":6,\n  \"fresh garlic\":5,\n  \"frozen peas\":8,\n  \"butter\":58,\n  \"salmon\":2,\n  \"orange juice\":8,\n  \"black pepper\":105,\n  \"lemon\":16,\n  \"tumeric\":3,\n  \"black olives\":11,\n  \"buttermilk\":4,\n  \"eggplant\":24,\n  \"capers\":22,\n  \"cornmeal\":14,\n  \"cloves\":9,\n  \"oil\":35,\n  \"chicken\":15,\n  \"blue cheese\":2,\n  \"cucumber\":9,\n  \"flour tortilla\":4,\n  \"canola oil\":16,\n  \"dried tarragon\":12,\n  \"celery\":18,\n  \"walnuts\":5,\n  \"nutmeg\":7,\n  \"fresh cilantro\":15,\n  \"bread crumbs\":14,\n  \"baking soda\":5,\n  \"tabasco sauce\":10,\n  \"balsamic vinegar\":21,\n  \"fresh rosemary\":2,\n  \"lime\":12,\n  \"pecans\":2,\n  \"peanut butter\":2,\n  \"cumin\":28,\n  \"green onions\":28,\n  \"canned chicken broth\":6,\n  \"beets\":2,\n  \"skim milk\":14,\n  \"barley\":4,\n  \"kidney beans\":9,\n  \"applesauce\":3,\n  \"parmesan cheese\":66,\n  \"carrots\":43,\n  \"mushrooms\":28,\n  \"sour cream\":21,\n  \"yogurt\":5,\n  \"brussels sprouts\":2,\n  \"cream cheese\":6,\n  \"cherry tomatoes\":4,\n  \"salsa\":13,\n  \"parsley\":28,\n  \"green olives\":3,\n  \"rice vinegar\":3,\n  \"molasses\":3,\n  \"beer\":10,\n  \"chili powder\":107,\n  \"spaghetti\":15,\n  \"raisins\":10,\n  \"red wine vinegar\":48,\n  \"cornstarch\":17,\n  \"orange\":3,\n  \"dried basil\":253,\n  \"radishes\":2,\n  \"catsup\":5,\n  \"ground beef\":37,\n  \"mozzarella cheese\":37,\n  \"sweet red pepper\":5,\n  \"cumin seeds\":11,\n  \"barbecue sauce\":2,\n  \"red pepper flakes\":17,\n  \"almonds\":3,\n  \"eggs\":51,\n  \"margarine\":11,\n  \"fresh ginger\":3,\n  \"kale\":2,\n  \"garlic powder\":108,\n  \"dried thyme\":140,\n  \"cream of mushroom soup\":2,\n  \"whole wheat flour\":7,\n  \"salt\":472,\n  \"fresh basil\":19,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":25,\n  \"frozen spinach\":3,\n  \"allspice\":6,\n  \"dijon mustard\":25,\n  \"soy sauce\":14,\n  \"broccoli\":7,\n  \"chicken legs\":3,\n  \"white rice\":6,\n  \"sweet potatoes\":2,\n  \"red leaf lettuce\":2,\n  \"egg whites\":11,\n  \"worcestershire sauce\":36,\n  \"brown sugar\":15,\n  \"lime juice\":11,\n  \"dry mustard\":14,\n  \"raw shrimp\":2,\n  \"white flour\":3,\n  \"canned vegetable broth\":2,\n  \"mayonnaise\":15,\n  \"chicken wings\":3,\n  \"olive oil\":358,\n  \"vegetable oil\":87,\n  \"fresh thyme\":3,\n  \"flour\":40,\n  \"curry powder\":2,\n  \"vinegar\":16,\n  \"white wine\":16,\n  \"sherry\":6,\n  \"whipping cream\":5,\n  \"chicken breast\":9,\n  \"sugar\":98},\n \"bread\":\n {\"red wine\":2,\n  \"elbow macaroni\":2,\n  \"bacon\":8,\n  \"cayenne pepper\":7,\n  \"lemon juice\":23,\n  \"honey\":7,\n  \"swiss cheese\":4,\n  \"paprika\":23,\n  \"ground cinnamon\":7,\n  \"egg yolks\":8,\n  \"green pepper\":10,\n  \"baking powder\":9,\n  \"saffron\":2,\n  \"pine nuts\":2,\n  \"cinnamon\":18,\n  \"cheddar cheese\":12,\n  \"tomatoes\":10,\n  \"white pepper\":2,\n  \"cauliflower\":2,\n  \"butter\":93,\n  \"frozen peas\":2,\n  \"orange juice\":7,\n  \"black pepper\":12,\n  \"lemon\":5,\n  \"powdered sugar\":5,\n  \"dried oregano\":3,\n  \"eggplant\":2,\n  \"capers\":5,\n  \"cornmeal\":2,\n  \"oil\":18,\n  \"chicken\":4,\n  \"cucumber\":4,\n  \"canola oil\":3,\n  \"celery\":20,\n  \"alfalfa sprouts\":2,\n  \"walnuts\":3,\n  \"nutmeg\":15,\n  \"bread crumbs\":4,\n  \"baking soda\":2,\n  \"tabasco sauce\":4,\n  \"fresh rosemary\":2,\n  \"pecans\":3,\n  \"peanut butter\":3,\n  \"green onions\":4,\n  \"cumin\":2,\n  \"skim milk\":7,\n  \"banana\":2,\n  \"shortening\":3,\n  \"parmesan cheese\":15,\n  \"carrots\":6,\n  \"mushrooms\":7,\n  \"sour cream\":6,\n  \"tofu\":2,\n  \"cream cheese\":8,\n  \"parsley\":29,\n  \"green olives\":2,\n  \"molasses\":3,\n  \"chili powder\":7,\n  \"beer\":3,\n  \"raisins\":16,\n  \"red wine vinegar\":3,\n  \"chives\":3,\n  \"cornstarch\":7,\n  \"orange\":2,\n  \"radishes\":2,\n  \"catsup\":4,\n  \"ground beef\":18,\n  \"mozzarella cheese\":2,\n  \"sweet red pepper\":2,\n  \"canned tuna\":2,\n  \"cumin seeds\":2,\n  \"barbecue sauce\":2,\n  \"almonds\":3,\n  \"eggs\":85,\n  \"margarine\":11,\n  \"garlic powder\":5,\n  \"dried thyme\":3,\n  \"cream of mushroom soup\":5,\n  \"vanilla\":21,\n  \"whole wheat flour\":2,\n  \"salt\":131,\n  \"potatoes\":10,\n  \"allspice\":7,\n  \"dijon mustard\":3,\n  \"soy sauce\":5,\n  \"apples\":12,\n  \"maple syrup\":7,\n  \"egg whites\":4,\n  \"worcestershire sauce\":11,\n  \"brown sugar\":10,\n  \"dry mustard\":18,\n  \"blueberries\":2,\n  \"mayonnaise\":21,\n  \"olive oil\":25,\n  \"vegetable oil\":9,\n  \"flour\":32,\n  \"curry powder\":5,\n  \"vinegar\":4,\n  \"white wine\":4,\n  \"sherry\":3,\n  \"whipping cream\":4,\n  \"sugar\":46},\n \"chicken breast\":\n {\"red wine\":2,\n  \"bacon\":2,\n  \"cayenne pepper\":8,\n  \"lemon juice\":21,\n  \"honey\":8,\n  \"swiss cheese\":3,\n  \"paprika\":18,\n  \"yeast\":3,\n  \"egg yolks\":3,\n  \"green pepper\":12,\n  \"baking powder\":3,\n  \"saffron\":2,\n  \"sesame oil\":21,\n  \"cinnamon\":8,\n  \"cheddar cheese\":6,\n  \"tomatoes\":13,\n  \"white pepper\":9,\n  \"black beans\":2,\n  \"fresh garlic\":2,\n  \"cauliflower\":2,\n  \"frozen peas\":2,\n  \"butter\":19,\n  \"orange juice\":6,\n  \"black pepper\":13,\n  \"lemon\":5,\n  \"black olives\":2,\n  \"powdered sugar\":2,\n  \"eggplant\":2,\n  \"dried oregano\":9,\n  \"hoisin sauce\":2,\n  \"capers\":3,\n  \"cloves\":3,\n  \"oil\":82,\n  \"chicken\":2,\n  \"blue cheese\":2,\n  \"cucumber\":2,\n  \"canola oil\":6,\n  \"flour tortilla\":2,\n  \"dried tarragon\":3,\n  \"celery\":15,\n  \"fresh cilantro\":3,\n  \"bread crumbs\":4,\n  \"tabasco sauce\":4,\n  \"ginger\":21,\n  \"balsamic vinegar\":3,\n  \"lime\":4,\n  \"pecans\":5,\n  \"peanut butter\":3,\n  \"green onions\":18,\n  \"cumin\":11,\n  \"frozen corn\":2,\n  \"canned chicken broth\":3,\n  \"skim milk\":2,\n  \"beets\":2,\n  \"shortening\":2,\n  \"parmesan cheese\":12,\n  \"carrots\":10,\n  \"mushrooms\":14,\n  \"sour cream\":13,\n  \"yogurt\":6,\n  \"cream cheese\":2,\n  \"salsa\":5,\n  \"parsley\":5,\n  \"beer\":2,\n  \"chili powder\":5,\n  \"spaghetti\":2,\n  \"raisins\":3,\n  \"red wine vinegar\":5,\n  \"garam masala\":2,\n  \"cornstarch\":86,\n  \"dried basil\":3,\n  \"radishes\":2,\n  \"catsup\":6,\n  \"mozzarella cheese\":7,\n  \"romaine lettuce\":2,\n  \"sweet red pepper\":4,\n  \"cumin seeds\":3,\n  \"red pepper flakes\":2,\n  \"almonds\":3,\n  \"eggs\":13,\n  \"margarine\":11,\n  \"fresh ginger\":3,\n  \"garlic powder\":9,\n  \"dried thyme\":2,\n  \"cream of mushroom soup\":2,\n  \"salt\":125,\n  \"fresh basil\":4,\n  \"bean sprouts\":10,\n  \"shiitake mushrooms\":3,\n  \"potatoes\":3,\n  \"allspice\":3,\n  \"dijon mustard\":6,\n  \"soy sauce\":85,\n  \"broccoli\":3,\n  \"apples\":2,\n  \"white rice\":2,\n  \"maple syrup\":2,\n  \"egg whites\":6,\n  \"worcestershire sauce\":4,\n  \"brown sugar\":8,\n  \"lime juice\":7,\n  \"dry mustard\":4,\n  \"raw shrimp\":2,\n  \"mayonnaise\":6,\n  \"olive oil\":35,\n  \"vegetable oil\":21,\n  \"curry powder\":11,\n  \"flour\":24,\n  \"vinegar\":12,\n  \"white wine\":6,\n  \"sherry\":34,\n  \"sugar\":61},\n \"dried tarragon\":\n {\"sour cream\":4,\n  \"yogurt\":2,\n  \"cream cheese\":2,\n  \"bacon\":4,\n  \"cayenne pepper\":7,\n  \"parsley\":7,\n  \"green olives\":3,\n  \"rice vinegar\":2,\n  \"lemon juice\":34,\n  \"honey\":11,\n  \"molasses\":2,\n  \"chili powder\":2,\n  \"paprika\":12,\n  \"ground cinnamon\":2,\n  \"raisins\":2,\n  \"red wine vinegar\":10,\n  \"chives\":5,\n  \"egg yolks\":2,\n  \"green pepper\":3,\n  \"baking powder\":3,\n  \"cornstarch\":5,\n  \"dried basil\":11,\n  \"sesame oil\":2,\n  \"pine nuts\":3,\n  \"cinnamon\":2,\n  \"tomatoes\":6,\n  \"white pepper\":10,\n  \"brown rice\":3,\n  \"eggs\":13,\n  \"margarine\":4,\n  \"butter\":33,\n  \"garlic powder\":10,\n  \"dried thyme\":22,\n  \"orange juice\":3,\n  \"whole wheat flour\":2,\n  \"lemon\":6,\n  \"salt\":72,\n  \"black pepper\":13,\n  \"black olives\":2,\n  \"potatoes\":4,\n  \"dried oregano\":12,\n  \"capers\":5,\n  \"oil\":3,\n  \"chicken\":4,\n  \"cucumber\":3,\n  \"canola oil\":2,\n  \"allspice\":2,\n  \"celery seeds\":2,\n  \"dijon mustard\":33,\n  \"soy sauce\":7,\n  \"celery\":8,\n  \"apples\":2,\n  \"alfalfa sprouts\":2,\n  \"fresh cilantro\":2,\n  \"nutmeg\":4,\n  \"bread crumbs\":4,\n  \"worcestershire sauce\":6,\n  \"tabasco sauce\":3,\n  \"brown sugar\":7,\n  \"ginger\":2,\n  \"balsamic vinegar\":6,\n  \"lime juice\":3,\n  \"dry mustard\":13,\n  \"raw shrimp\":2,\n  \"mayonnaise\":12,\n  \"cumin\":2,\n  \"green onions\":9,\n  \"fresh thyme\":2,\n  \"vegetable oil\":14,\n  \"olive oil\":46,\n  \"beets\":2,\n  \"skim milk\":4,\n  \"curry powder\":3,\n  \"flour\":11,\n  \"vinegar\":4,\n  \"parmesan cheese\":3,\n  \"white wine\":4,\n  \"carrots\":5,\n  \"sherry\":4,\n  \"whipping cream\":8,\n  \"chicken breast\":3,\n  \"mushrooms\":14,\n  \"sugar\":13},\n \"pecans\":\n {\"cayenne pepper\":13,\n  \"lemon juice\":41,\n  \"honey\":24,\n  \"paprika\":15,\n  \"ground cinnamon\":45,\n  \"bread\":3,\n  \"yeast\":17,\n  \"egg yolks\":24,\n  \"green pepper\":3,\n  \"baking powder\":138,\n  \"sesame oil\":2,\n  \"pine nuts\":2,\n  \"cinnamon\":131,\n  \"cream of tartar\":9,\n  \"cheddar cheese\":5,\n  \"white pepper\":3,\n  \"brown rice\":3,\n  \"butter\":328,\n  \"orange juice\":23,\n  \"black pepper\":14,\n  \"lemon\":6,\n  \"black olives\":4,\n  \"buttermilk\":38,\n  \"powdered sugar\":55,\n  \"hoisin sauce\":2,\n  \"dried oregano\":2,\n  \"capers\":2,\n  \"cornmeal\":3,\n  \"cloves\":24,\n  \"oil\":42,\n  \"chicken\":6,\n  \"canola oil\":3,\n  \"strawberries\":6,\n  \"celery\":19,\n  \"nutmeg\":53,\n  \"walnuts\":14,\n  \"baking soda\":133,\n  \"bread crumbs\":5,\n  \"corn syrup\":21,\n  \"tabasco sauce\":4,\n  \"balsamic vinegar\":2,\n  \"ginger\":7,\n  \"fresh rosemary\":3,\n  \"peanut butter\":7,\n  \"green onions\":8,\n  \"cumin\":2,\n  \"skim milk\":5,\n  \"banana\":9,\n  \"shortening\":34,\n  \"applesauce\":10,\n  \"parmesan cheese\":2,\n  \"carrots\":12,\n  \"mushrooms\":9,\n  \"sour cream\":42,\n  \"yogurt\":6,\n  \"tofu\":2,\n  \"cream cheese\":63,\n  \"cherry tomatoes\":3,\n  \"salsa\":4,\n  \"parsley\":14,\n  \"molasses\":9,\n  \"chili powder\":3,\n  \"beer\":3,\n  \"raisins\":64,\n  \"red wine vinegar\":3,\n  \"chives\":3,\n  \"cornstarch\":24,\n  \"dried basil\":3,\n  \"orange\":9,\n  \"catsup\":2,\n  \"ground beef\":2,\n  \"mozzarella cheese\":2,\n  \"eggs\":304,\n  \"almonds\":7,\n  \"margarine\":66,\n  \"garlic powder\":4,\n  \"dried thyme\":5,\n  \"vanilla\":244,\n  \"whole wheat flour\":15,\n  \"salt\":374,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":6,\n  \"almond extract\":10,\n  \"frozen spinach\":3,\n  \"allspice\":23,\n  \"dijon mustard\":4,\n  \"soy sauce\":4,\n  \"broccoli\":4,\n  \"apples\":26,\n  \"maple syrup\":14,\n  \"sweet potatoes\":12,\n  \"egg whites\":32,\n  \"worcestershire sauce\":9,\n  \"brown sugar\":151,\n  \"lime juice\":4,\n  \"dry mustard\":5,\n  \"blueberries\":5,\n  \"mayonnaise\":24,\n  \"vegetable oil\":25,\n  \"olive oil\":13,\n  \"fresh thyme\":4,\n  \"curry powder\":2,\n  \"flour\":257,\n  \"vinegar\":9,\n  \"sherry\":3,\n  \"oats\":8,\n  \"whipping cream\":33,\n  \"chicken breast\":5,\n  \"sugar\":474},\n \"tomatoes\":\n {\"elbow macaroni\":4,\n  \"red wine\":37,\n  \"canned tomato sauce\":2,\n  \"coconut oil\":2,\n  \"bacon\":69,\n  \"cayenne pepper\":100,\n  \"lemon juice\":194,\n  \"honey\":19,\n  \"swiss cheese\":17,\n  \"paprika\":143,\n  \"bread\":10,\n  \"ground cinnamon\":23,\n  \"egg yolks\":9,\n  \"green pepper\":245,\n  \"baking powder\":5,\n  \"saffron\":26,\n  \"sesame oil\":9,\n  \"cream of tartar\":2,\n  \"cinnamon\":70,\n  \"pine nuts\":16,\n  \"avocado\":38,\n  \"cheddar cheese\":62,\n  \"corn tortilla\":2,\n  \"white pepper\":28,\n  \"brown rice\":28,\n  \"black beans\":32,\n  \"fresh garlic\":2,\n  \"pinto beans\":23,\n  \"cauliflower\":23,\n  \"frozen peas\":19,\n  \"butter\":259,\n  \"salmon\":9,\n  \"orange juice\":14,\n  \"lemon\":55,\n  \"black pepper\":187,\n  \"tumeric\":13,\n  \"black olives\":52,\n  \"buttermilk\":3,\n  \"hoisin sauce\":2,\n  \"eggplant\":94,\n  \"dried oregano\":90,\n  \"capers\":37,\n  \"cornmeal\":5,\n  \"cloves\":41,\n  \"oil\":250,\n  \"chicken\":53,\n  \"blue cheese\":10,\n  \"cucumber\":55,\n  \"canola oil\":23,\n  \"dried tarragon\":6,\n  \"celery\":157,\n  \"cornish game hens\":2,\n  \"alfalfa sprouts\":5,\n  \"walnuts\":5,\n  \"fresh cilantro\":41,\n  \"nutmeg\":25,\n  \"baking soda\":4,\n  \"bread crumbs\":30,\n  \"corn syrup\":2,\n  \"tabasco sauce\":50,\n  \"balsamic vinegar\":43,\n  \"ginger\":40,\n  \"fresh rosemary\":6,\n  \"lime\":12,\n  \"peanut butter\":8,\n  \"frozen corn\":9,\n  \"green onions\":121,\n  \"cumin\":177,\n  \"canned chicken broth\":2,\n  \"banana\":4,\n  \"beets\":3,\n  \"skim milk\":5,\n  \"barley\":16,\n  \"kidney beans\":59,\n  \"shortening\":15,\n  \"parmesan cheese\":85,\n  \"carrots\":195,\n  \"mushrooms\":122,\n  \"sour cream\":87,\n  \"yogurt\":15,\n  \"brussels sprouts\":2,\n  \"tofu\":13,\n  \"cream cheese\":16,\n  \"cherry tomatoes\":5,\n  \"salsa\":15,\n  \"parsley\":245,\n  \"green olives\":16,\n  \"rice vinegar\":4,\n  \"molasses\":9,\n  \"chili powder\":249,\n  \"beer\":20,\n  \"spaghetti\":23,\n  \"raisins\":32,\n  \"red wine vinegar\":83,\n  \"garam masala\":43,\n  \"chives\":23,\n  \"cornstarch\":64,\n  \"orange\":6,\n  \"dried basil\":77,\n  \"radishes\":7,\n  \"catsup\":8,\n  \"ground beef\":133,\n  \"romaine lettuce\":3,\n  \"mozzarella cheese\":36,\n  \"sweet red pepper\":13,\n  \"cumin seeds\":34,\n  \"barbecue sauce\":2,\n  \"raw spinach\":2,\n  \"red pepper flakes\":26,\n  \"eggs\":126,\n  \"almonds\":18,\n  \"margarine\":37,\n  \"fresh ginger\":16,\n  \"kale\":2,\n  \"garlic powder\":77,\n  \"dried thyme\":48,\n  \"cream of mushroom soup\":4,\n  \"whole wheat flour\":2,\n  \"salt\":1322,\n  \"fresh basil\":52,\n  \"bean sprouts\":4,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":153,\n  \"frozen spinach\":7,\n  \"celery seeds\":4,\n  \"allspice\":32,\n  \"dijon mustard\":23,\n  \"soy sauce\":61,\n  \"broccoli\":14,\n  \"chicken legs\":4,\n  \"apples\":10,\n  \"maple syrup\":3,\n  \"shell macaroni\":4,\n  \"yellow mustard\":2,\n  \"sweet potatoes\":9,\n  \"white rice\":7,\n  \"canned tomato paste\":2,\n  \"egg whites\":7,\n  \"worcestershire sauce\":70,\n  \"dried cilantro\":2,\n  \"brown sugar\":55,\n  \"lime juice\":75,\n  \"dry mustard\":25,\n  \"raw shrimp\":4,\n  \"white flour\":3,\n  \"mayonnaise\":52,\n  \"chicken wings\":5,\n  \"fresh thyme\":11,\n  \"olive oil\":667,\n  \"vegetable oil\":196,\n  \"flour\":173,\n  \"curry powder\":49,\n  \"vinegar\":114,\n  \"white wine\":49,\n  \"sherry\":22,\n  \"oats\":2,\n  \"whipping cream\":13,\n  \"chicken breast\":13,\n  \"sugar\":272},\n \"red wine\":\n {\"bacon\":19,\n  \"cayenne pepper\":9,\n  \"lemon juice\":22,\n  \"honey\":10,\n  \"paprika\":21,\n  \"ground cinnamon\":8,\n  \"bread\":2,\n  \"egg yolks\":9,\n  \"green pepper\":19,\n  \"baking powder\":3,\n  \"saffron\":2,\n  \"sesame oil\":8,\n  \"pine nuts\":3,\n  \"cinnamon\":13,\n  \"avocado\":2,\n  \"cheddar cheese\":2,\n  \"tomatoes\":37,\n  \"white pepper\":10,\n  \"black beans\":4,\n  \"fresh garlic\":2,\n  \"pinto beans\":2,\n  \"frozen peas\":3,\n  \"butter\":75,\n  \"orange juice\":8,\n  \"lemon\":10,\n  \"black pepper\":44,\n  \"black olives\":4,\n  \"buttermilk\":3,\n  \"dried oregano\":12,\n  \"eggplant\":6,\n  \"capers\":7,\n  \"cloves\":20,\n  \"oil\":33,\n  \"chicken\":10,\n  \"cucumber\":2,\n  \"strawberries\":3,\n  \"celery\":11,\n  \"fresh cilantro\":2,\n  \"walnuts\":5,\n  \"nutmeg\":11,\n  \"bread crumbs\":6,\n  \"tabasco sauce\":3,\n  \"ginger\":4,\n  \"balsamic vinegar\":9,\n  \"fresh rosemary\":3,\n  \"lime\":3,\n  \"cumin\":16,\n  \"green onions\":8,\n  \"skim milk\":2,\n  \"barley\":2,\n  \"kidney beans\":4,\n  \"shortening\":2,\n  \"applesauce\":3,\n  \"parmesan cheese\":15,\n  \"carrots\":38,\n  \"mushrooms\":42,\n  \"sour cream\":14,\n  \"yogurt\":2,\n  \"cream cheese\":2,\n  \"salsa\":2,\n  \"parsley\":32,\n  \"molasses\":4,\n  \"beer\":2,\n  \"chili powder\":17,\n  \"spaghetti\":3,\n  \"raisins\":8,\n  \"red wine vinegar\":19,\n  \"chives\":3,\n  \"cornstarch\":10,\n  \"dried basil\":12,\n  \"orange\":6,\n  \"catsup\":3,\n  \"radishes\":2,\n  \"ground beef\":12,\n  \"mozzarella cheese\":4,\n  \"sweet red pepper\":2,\n  \"cumin seeds\":3,\n  \"barbecue sauce\":2,\n  \"red pepper flakes\":6,\n  \"eggs\":14,\n  \"almonds\":4,\n  \"margarine\":5,\n  \"fresh ginger\":2,\n  \"garlic powder\":7,\n  \"dried thyme\":13,\n  \"cream of mushroom soup\":3,\n  \"vanilla\":3,\n  \"salt\":165,\n  \"fresh basil\":10,\n  \"potatoes\":10,\n  \"almond extract\":2,\n  \"allspice\":9,\n  \"dijon mustard\":7,\n  \"soy sauce\":18,\n  \"broccoli\":2,\n  \"apples\":14,\n  \"chicken legs\":2,\n  \"sweet potatoes\":2,\n  \"white rice\":2,\n  \"maple syrup\":3,\n  \"egg whites\":2,\n  \"worcestershire sauce\":21,\n  \"brown sugar\":25,\n  \"lime juice\":7,\n  \"dry mustard\":6,\n  \"vegetable oil\":22,\n  \"fresh thyme\":3,\n  \"olive oil\":109,\n  \"flour\":68,\n  \"curry powder\":3,\n  \"vinegar\":17,\n  \"white wine\":7,\n  \"sherry\":5,\n  \"whipping cream\":3,\n  \"chicken breast\":2,\n  \"sugar\":58},\n \"chili powder\":\n {\"canned beef broth\":4,\n  \"elbow macaroni\":14,\n  \"red wine\":17,\n  \"canned tomato sauce\":5,\n  \"bacon\":26,\n  \"cayenne pepper\":182,\n  \"honey\":44,\n  \"lemon juice\":99,\n  \"zucchini squash\":2,\n  \"paprika\":281,\n  \"bread\":7,\n  \"ground cinnamon\":37,\n  \"yeast\":3,\n  \"egg yolks\":3,\n  \"green pepper\":204,\n  \"baking powder\":23,\n  \"saffron\":3,\n  \"sesame oil\":14,\n  \"pine nuts\":4,\n  \"cinnamon\":62,\n  \"avocado\":20,\n  \"cheddar cheese\":88,\n  \"corn tortilla\":2,\n  \"tomatoes\":249,\n  \"white pepper\":39,\n  \"brown rice\":18,\n  \"black beans\":58,\n  \"pinto beans\":59,\n  \"cauliflower\":3,\n  \"frozen peas\":5,\n  \"butter\":102,\n  \"orange juice\":15,\n  \"black pepper\":229,\n  \"lemon\":23,\n  \"tumeric\":14,\n  \"black olives\":21,\n  \"buttermilk\":9,\n  \"dried oregano\":107,\n  \"eggplant\":12,\n  \"capers\":3,\n  \"cornmeal\":35,\n  \"cloves\":34,\n  \"oil\":158,\n  \"chicken\":18,\n  \"blue cheese\":4,\n  \"cucumber\":8,\n  \"canola oil\":27,\n  \"dried tarragon\":2,\n  \"strawberries\":2,\n  \"celery\":78,\n  \"alfalfa sprouts\":2,\n  \"walnuts\":4,\n  \"fresh cilantro\":39,\n  \"nutmeg\":18,\n  \"baking soda\":12,\n  \"bread crumbs\":9,\n  \"corn syrup\":3,\n  \"tabasco sauce\":77,\n  \"balsamic vinegar\":5,\n  \"ginger\":34,\n  \"lime\":8,\n  \"pecans\":3,\n  \"peanut butter\":20,\n  \"cumin\":403,\n  \"green onions\":66,\n  \"frozen corn\":17,\n  \"canned chicken broth\":2,\n  \"banana\":2,\n  \"skim milk\":17,\n  \"kidney beans\":104,\n  \"shortening\":14,\n  \"applesauce\":3,\n  \"parmesan cheese\":10,\n  \"carrots\":49,\n  \"mushrooms\":24,\n  \"sour cream\":74,\n  \"yogurt\":14,\n  \"tofu\":14,\n  \"cream cheese\":12,\n  \"cherry tomatoes\":5,\n  \"salsa\":50,\n  \"parsley\":48,\n  \"green olives\":5,\n  \"molasses\":32,\n  \"beer\":45,\n  \"spaghetti\":12,\n  \"raisins\":21,\n  \"red wine vinegar\":44,\n  \"chives\":6,\n  \"garam masala\":21,\n  \"cornstarch\":28,\n  \"dried basil\":22,\n  \"catsup\":65,\n  \"ground beef\":250,\n  \"romaine lettuce\":2,\n  \"mozzarella cheese\":3,\n  \"sweet red pepper\":13,\n  \"cumin seeds\":27,\n  \"barbecue sauce\":16,\n  \"red pepper flakes\":21,\n  \"almonds\":5,\n  \"eggs\":48,\n  \"margarine\":37,\n  \"fresh ginger\":7,\n  \"garlic powder\":262,\n  \"dried thyme\":30,\n  \"cream of mushroom soup\":15,\n  \"whole wheat flour\":6,\n  \"salt\":1111,\n  \"fresh basil\":2,\n  \"bean sprouts\":4,\n  \"potatoes\":29,\n  \"frozen spinach\":2,\n  \"allspice\":38,\n  \"dijon mustard\":22,\n  \"soy sauce\":58,\n  \"broccoli\":3,\n  \"apples\":7,\n  \"chicken legs\":2,\n  \"yellow mustard\":2,\n  \"sweet potatoes\":5,\n  \"white rice\":5,\n  \"maple syrup\":7,\n  \"egg whites\":5,\n  \"worcestershire sauce\":190,\n  \"dried cilantro\":3,\n  \"brown sugar\":142,\n  \"lime juice\":58,\n  \"dry mustard\":94,\n  \"raw shrimp\":2,\n  \"white flour\":4,\n  \"canned vegetable broth\":2,\n  \"mayonnaise\":17,\n  \"chicken wings\":10,\n  \"fresh thyme\":2,\n  \"vegetable oil\":221,\n  \"olive oil\":232,\n  \"flour\":126,\n  \"curry powder\":46,\n  \"vinegar\":134,\n  \"white wine\":9,\n  \"sherry\":6,\n  \"oats\":2,\n  \"whipping cream\":4,\n  \"chicken breast\":5,\n  \"sugar\":197},\n \"catsup\":\n {\"red wine\":3,\n  \"elbow macaroni\":2,\n  \"canned tomato sauce\":4,\n  \"bacon\":32,\n  \"cayenne pepper\":21,\n  \"lemon juice\":78,\n  \"honey\":38,\n  \"paprika\":60,\n  \"ground cinnamon\":4,\n  \"bread\":4,\n  \"egg yolks\":2,\n  \"green pepper\":22,\n  \"baking powder\":3,\n  \"sesame oil\":5,\n  \"cinnamon\":11,\n  \"avocado\":4,\n  \"cheddar cheese\":5,\n  \"tomatoes\":8,\n  \"white pepper\":5,\n  \"fresh garlic\":2,\n  \"cauliflower\":2,\n  \"butter\":41,\n  \"frozen peas\":2,\n  \"orange juice\":5,\n  \"lemon\":14,\n  \"black pepper\":42,\n  \"tumeric\":3,\n  \"powdered sugar\":2,\n  \"hoisin sauce\":13,\n  \"dried oregano\":5,\n  \"capers\":2,\n  \"cornmeal\":2,\n  \"cloves\":5,\n  \"oil\":58,\n  \"chicken\":12,\n  \"blue cheese\":4,\n  \"cucumber\":2,\n  \"canola oil\":2,\n  \"celery\":23,\n  \"nutmeg\":9,\n  \"bread crumbs\":10,\n  \"baking soda\":4,\n  \"tabasco sauce\":20,\n  \"ginger\":10,\n  \"balsamic vinegar\":2,\n  \"pecans\":2,\n  \"peanut butter\":2,\n  \"cumin\":11,\n  \"green onions\":10,\n  \"canned chicken broth\":2,\n  \"beets\":2,\n  \"barley\":3,\n  \"kidney beans\":5,\n  \"shortening\":10,\n  \"applesauce\":4,\n  \"parmesan cheese\":4,\n  \"carrots\":23,\n  \"mushrooms\":11,\n  \"sour cream\":14,\n  \"cream cheese\":6,\n  \"cherry tomatoes\":3,\n  \"salsa\":4,\n  \"parsley\":12,\n  \"green olives\":2,\n  \"molasses\":31,\n  \"chili powder\":65,\n  \"beer\":11,\n  \"raisins\":4,\n  \"red wine vinegar\":17,\n  \"chives\":3,\n  \"cornstarch\":49,\n  \"orange\":3,\n  \"dried basil\":4,\n  \"ground beef\":68,\n  \"mozzarella cheese\":2,\n  \"sweet red pepper\":2,\n  \"barbecue sauce\":3,\n  \"red pepper flakes\":2,\n  \"eggs\":26,\n  \"margarine\":9,\n  \"fresh ginger\":2,\n  \"garlic powder\":40,\n  \"dried thyme\":5,\n  \"cream of mushroom soup\":5,\n  \"low fat milk\":2,\n  \"whole wheat flour\":2,\n  \"salt\":287,\n  \"bean sprouts\":2,\n  \"potatoes\":11,\n  \"allspice\":8,\n  \"celery seeds\":2,\n  \"dijon mustard\":6,\n  \"soy sauce\":90,\n  \"chicken legs\":2,\n  \"maple syrup\":4,\n  \"yellow mustard\":3,\n  \"sweet potatoes\":2,\n  \"egg whites\":3,\n  \"worcestershire sauce\":199,\n  \"brown sugar\":159,\n  \"lime juice\":6,\n  \"dry mustard\":67,\n  \"white flour\":2,\n  \"mayonnaise\":25,\n  \"chicken wings\":13,\n  \"vegetable oil\":38,\n  \"olive oil\":11,\n  \"flour\":43,\n  \"curry powder\":7,\n  \"vinegar\":146,\n  \"white wine\":8,\n  \"sherry\":17,\n  \"oats\":2,\n  \"whipping cream\":3,\n  \"chicken breast\":6,\n  \"sugar\":96},\n \"apples\":\n {\"elbow macaroni\":2,\n  \"red wine\":14,\n  \"bacon\":7,\n  \"cayenne pepper\":6,\n  \"honey\":50,\n  \"lemon juice\":109,\n  \"paprika\":8,\n  \"ground cinnamon\":58,\n  \"bread\":12,\n  \"yeast\":8,\n  \"egg yolks\":10,\n  \"green pepper\":5,\n  \"baking powder\":115,\n  \"cinnamon\":331,\n  \"cream of tartar\":2,\n  \"cheddar cheese\":7,\n  \"tomatoes\":10,\n  \"brown rice\":3,\n  \"butter\":187,\n  \"salmon\":2,\n  \"orange juice\":33,\n  \"black pepper\":15,\n  \"lemon\":27,\n  \"buttermilk\":7,\n  \"powdered sugar\":17,\n  \"cornmeal\":2,\n  \"cloves\":47,\n  \"oil\":54,\n  \"chicken\":7,\n  \"cucumber\":2,\n  \"canola oil\":10,\n  \"dried tarragon\":2,\n  \"strawberries\":6,\n  \"celery\":22,\n  \"alfalfa sprouts\":2,\n  \"cornish game hens\":3,\n  \"nutmeg\":108,\n  \"walnuts\":41,\n  \"baking soda\":83,\n  \"bread crumbs\":13,\n  \"corn syrup\":7,\n  \"tabasco sauce\":3,\n  \"ginger\":23,\n  \"pecans\":26,\n  \"peanut butter\":4,\n  \"green onions\":4,\n  \"cumin\":4,\n  \"beets\":2,\n  \"skim milk\":11,\n  \"banana\":6,\n  \"kidney beans\":2,\n  \"shortening\":25,\n  \"parmesan cheese\":2,\n  \"applesauce\":20,\n  \"carrots\":24,\n  \"mushrooms\":5,\n  \"sour cream\":17,\n  \"yogurt\":9,\n  \"cream cheese\":11,\n  \"salsa\":2,\n  \"parsley\":10,\n  \"molasses\":9,\n  \"chili powder\":7,\n  \"beer\":4,\n  \"raisins\":110,\n  \"red wine vinegar\":5,\n  \"cornstarch\":40,\n  \"orange\":15,\n  \"radishes\":3,\n  \"ground beef\":6,\n  \"sweet red pepper\":2,\n  \"eggs\":130,\n  \"almonds\":14,\n  \"margarine\":57,\n  \"fresh ginger\":3,\n  \"garlic powder\":3,\n  \"dried thyme\":5,\n  \"vanilla\":113,\n  \"whole wheat flour\":16,\n  \"salt\":303,\n  \"potatoes\":11,\n  \"almond extract\":6,\n  \"celery seeds\":2,\n  \"allspice\":24,\n  \"dijon mustard\":6,\n  \"sweet potatoes\":14,\n  \"maple syrup\":12,\n  \"egg whites\":28,\n  \"worcestershire sauce\":6,\n  \"brown sugar\":155,\n  \"lime juice\":5,\n  \"dry mustard\":7,\n  \"white flour\":2,\n  \"blueberries\":7,\n  \"mayonnaise\":16,\n  \"chicken wings\":2,\n  \"vegetable oil\":28,\n  \"olive oil\":13,\n  \"flour\":225,\n  \"curry powder\":29,\n  \"vinegar\":35,\n  \"white wine\":9,\n  \"sherry\":4,\n  \"oats\":10,\n  \"whipping cream\":10,\n  \"chicken breast\":2,\n  \"sugar\":384},\n \"black olives\":\n {\"red wine\":4,\n  \"elbow macaroni\":2,\n  \"bacon\":3,\n  \"cayenne pepper\":5,\n  \"lemon juice\":22,\n  \"honey\":4,\n  \"swiss cheese\":2,\n  \"paprika\":8,\n  \"ground cinnamon\":2,\n  \"yeast\":3,\n  \"green pepper\":27,\n  \"baking powder\":5,\n  \"saffron\":2,\n  \"pine nuts\":12,\n  \"cinnamon\":3,\n  \"avocado\":4,\n  \"cheddar cheese\":19,\n  \"tomatoes\":52,\n  \"white pepper\":3,\n  \"brown rice\":2,\n  \"black beans\":4,\n  \"fresh garlic\":2,\n  \"pinto beans\":4,\n  \"cauliflower\":3,\n  \"frozen peas\":2,\n  \"butter\":21,\n  \"black pepper\":15,\n  \"lemon\":9,\n  \"eggplant\":8,\n  \"dried oregano\":11,\n  \"capers\":28,\n  \"cornmeal\":3,\n  \"cloves\":3,\n  \"oil\":13,\n  \"chicken\":5,\n  \"blue cheese\":4,\n  \"cucumber\":6,\n  \"canola oil\":2,\n  \"dried tarragon\":2,\n  \"celery\":19,\n  \"alfalfa sprouts\":2,\n  \"fresh cilantro\":4,\n  \"nutmeg\":2,\n  \"bread crumbs\":3,\n  \"tabasco sauce\":7,\n  \"balsamic vinegar\":5,\n  \"fresh rosemary\":3,\n  \"pecans\":4,\n  \"cumin\":13,\n  \"green onions\":28,\n  \"kidney beans\":3,\n  \"shortening\":3,\n  \"parmesan cheese\":19,\n  \"carrots\":7,\n  \"mushrooms\":14,\n  \"sour cream\":24,\n  \"yogurt\":2,\n  \"tofu\":2,\n  \"cream cheese\":26,\n  \"cherry tomatoes\":3,\n  \"salsa\":6,\n  \"parsley\":26,\n  \"green olives\":21,\n  \"chili powder\":21,\n  \"spaghetti\":3,\n  \"raisins\":4,\n  \"red wine vinegar\":13,\n  \"chives\":4,\n  \"cornstarch\":2,\n  \"dried basil\":11,\n  \"radishes\":2,\n  \"ground beef\":5,\n  \"romaine lettuce\":3,\n  \"mozzarella cheese\":5,\n  \"red pepper flakes\":4,\n  \"eggs\":22,\n  \"margarine\":7,\n  \"fresh ginger\":2,\n  \"garlic powder\":11,\n  \"dried thyme\":2,\n  \"whole wheat flour\":3,\n  \"salt\":73,\n  \"fresh basil\":13,\n  \"potatoes\":7,\n  \"frozen spinach\":2,\n  \"dijon mustard\":3,\n  \"broccoli\":2,\n  \"maple syrup\":3,\n  \"worcestershire sauce\":5,\n  \"brown sugar\":5,\n  \"lime juice\":4,\n  \"dry mustard\":2,\n  \"raw shrimp\":2,\n  \"white flour\":2,\n  \"mayonnaise\":13,\n  \"fresh thyme\":2,\n  \"vegetable oil\":8,\n  \"olive oil\":116,\n  \"flour\":9,\n  \"curry powder\":4,\n  \"vinegar\":10,\n  \"white wine\":8,\n  \"sherry\":2,\n  \"whipping cream\":3,\n  \"chicken breast\":2,\n  \"sugar\":14},\n \"white pepper\":\n {\"canned beef broth\":2,\n  \"elbow macaroni\":7,\n  \"red wine\":10,\n  \"canned tomato sauce\":4,\n  \"bacon\":18,\n  \"cayenne pepper\":84,\n  \"honey\":29,\n  \"lemon juice\":108,\n  \"swiss cheese\":13,\n  \"paprika\":118,\n  \"bread\":2,\n  \"ground cinnamon\":5,\n  \"yeast\":2,\n  \"egg yolks\":22,\n  \"green pepper\":22,\n  \"baking powder\":14,\n  \"saffron\":8,\n  \"sesame oil\":54,\n  \"cream of tartar\":3,\n  \"pine nuts\":6,\n  \"cinnamon\":14,\n  \"avocado\":5,\n  \"cheddar cheese\":15,\n  \"tomatoes\":28,\n  \"brown rice\":7,\n  \"black beans\":4,\n  \"fresh garlic\":2,\n  \"pinto beans\":2,\n  \"cauliflower\":5,\n  \"frozen peas\":2,\n  \"butter\":192,\n  \"salmon\":2,\n  \"orange juice\":9,\n  \"lemon\":18,\n  \"black pepper\":134,\n  \"tumeric\":3,\n  \"black olives\":3,\n  \"buttermilk\":10,\n  \"powdered sugar\":2,\n  \"hoisin sauce\":4,\n  \"dried oregano\":13,\n  \"eggplant\":7,\n  \"capers\":7,\n  \"cornmeal\":5,\n  \"cloves\":12,\n  \"oil\":54,\n  \"chicken\":9,\n  \"blue cheese\":6,\n  \"cucumber\":12,\n  \"dry rosemary\":2,\n  \"flour tortilla\":2,\n  \"dried tarragon\":10,\n  \"strawberries\":2,\n  \"celery\":46,\n  \"alfalfa sprouts\":2,\n  \"cornish game hens\":2,\n  \"fresh cilantro\":8,\n  \"nutmeg\":62,\n  \"walnuts\":2,\n  \"bread crumbs\":26,\n  \"baking soda\":9,\n  \"tabasco sauce\":26,\n  \"balsamic vinegar\":8,\n  \"ginger\":23,\n  \"fresh rosemary\":2,\n  \"lime\":4,\n  \"pecans\":3,\n  \"peanut butter\":3,\n  \"frozen corn\":2,\n  \"cumin\":34,\n  \"green onions\":46,\n  \"canned chicken broth\":3,\n  \"banana\":2,\n  \"beets\":3,\n  \"skim milk\":17,\n  \"kidney beans\":2,\n  \"shortening\":3,\n  \"parmesan cheese\":33,\n  \"carrots\":39,\n  \"mushrooms\":38,\n  \"sour cream\":49,\n  \"yogurt\":5,\n  \"cream cheese\":20,\n  \"cherry tomatoes\":4,\n  \"salsa\":5,\n  \"parsley\":52,\n  \"green olives\":2,\n  \"rice vinegar\":11,\n  \"molasses\":5,\n  \"chili powder\":39,\n  \"beer\":5,\n  \"spaghetti\":2,\n  \"raisins\":7,\n  \"red wine vinegar\":19,\n  \"chives\":18,\n  \"garam masala\":4,\n  \"cornstarch\":95,\n  \"orange\":5,\n  \"dried basil\":15,\n  \"radishes\":3,\n  \"catsup\":5,\n  \"ground beef\":14,\n  \"mozzarella cheese\":9,\n  \"romaine lettuce\":2,\n  \"sweet red pepper\":5,\n  \"cumin seeds\":3,\n  \"barbecue sauce\":2,\n  \"red pepper flakes\":6,\n  \"almonds\":4,\n  \"eggs\":97,\n  \"margarine\":40,\n  \"fresh ginger\":9,\n  \"garlic powder\":120,\n  \"dried thyme\":38,\n  \"cream of mushroom soup\":4,\n  \"low fat milk\":2,\n  \"whole wheat flour\":4,\n  \"salt\":722,\n  \"fresh basil\":10,\n  \"bean sprouts\":4,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":31,\n  \"frozen spinach\":4,\n  \"celery seeds\":5,\n  \"allspice\":12,\n  \"dijon mustard\":28,\n  \"soy sauce\":70,\n  \"broccoli\":8,\n  \"white rice\":4,\n  \"sweet potatoes\":4,\n  \"egg whites\":12,\n  \"worcestershire sauce\":46,\n  \"dried cilantro\":2,\n  \"brown sugar\":25,\n  \"lime juice\":18,\n  \"dry mustard\":57,\n  \"raw shrimp\":2,\n  \"white flour\":2,\n  \"mayonnaise\":33,\n  \"chicken wings\":2,\n  \"fresh thyme\":4,\n  \"vegetable oil\":66,\n  \"olive oil\":126,\n  \"flour\":140,\n  \"curry powder\":29,\n  \"vinegar\":33,\n  \"white wine\":35,\n  \"sherry\":22,\n  \"whipping cream\":28,\n  \"chicken breast\":9,\n  \"sugar\":125},\n \"balsamic vinegar\":\n {\"red wine\":9,\n  \"bacon\":9,\n  \"cayenne pepper\":7,\n  \"honey\":24,\n  \"lemon juice\":47,\n  \"swiss cheese\":2,\n  \"paprika\":4,\n  \"ground cinnamon\":2,\n  \"egg yolks\":3,\n  \"green pepper\":2,\n  \"sesame oil\":9,\n  \"cinnamon\":4,\n  \"pine nuts\":11,\n  \"avocado\":2,\n  \"tomatoes\":43,\n  \"white pepper\":8,\n  \"brown rice\":3,\n  \"black beans\":7,\n  \"fresh garlic\":3,\n  \"cauliflower\":2,\n  \"frozen peas\":4,\n  \"butter\":43,\n  \"orange juice\":14,\n  \"black pepper\":45,\n  \"lemon\":11,\n  \"tumeric\":2,\n  \"black olives\":5,\n  \"buttermilk\":2,\n  \"eggplant\":22,\n  \"dried oregano\":21,\n  \"hoisin sauce\":4,\n  \"capers\":19,\n  \"cornmeal\":4,\n  \"cloves\":5,\n  \"oil\":10,\n  \"chicken\":4,\n  \"blue cheese\":4,\n  \"cucumber\":5,\n  \"canola oil\":8,\n  \"dried tarragon\":6,\n  \"strawberries\":8,\n  \"celery\":5,\n  \"cornish game hens\":2,\n  \"nutmeg\":2,\n  \"fresh cilantro\":3,\n  \"walnuts\":7,\n  \"bread crumbs\":8,\n  \"tabasco sauce\":4,\n  \"ginger\":8,\n  \"fresh rosemary\":5,\n  \"lime\":4,\n  \"pecans\":2,\n  \"green onions\":19,\n  \"frozen corn\":2,\n  \"cumin\":8,\n  \"banana\":3,\n  \"beets\":4,\n  \"barley\":3,\n  \"kidney beans\":2,\n  \"parmesan cheese\":16,\n  \"carrots\":17,\n  \"mushrooms\":20,\n  \"sour cream\":6,\n  \"brussels sprouts\":2,\n  \"tofu\":3,\n  \"cherry tomatoes\":11,\n  \"salsa\":2,\n  \"parsley\":24,\n  \"green olives\":2,\n  \"rice vinegar\":6,\n  \"molasses\":7,\n  \"chili powder\":5,\n  \"spaghetti\":2,\n  \"raisins\":7,\n  \"red wine vinegar\":13,\n  \"chives\":8,\n  \"cornstarch\":11,\n  \"dried basil\":22,\n  \"orange\":7,\n  \"radishes\":3,\n  \"catsup\":2,\n  \"romaine lettuce\":2,\n  \"mozzarella cheese\":8,\n  \"sweet red pepper\":5,\n  \"sweet red onions\":2,\n  \"red pepper flakes\":9,\n  \"eggs\":7,\n  \"margarine\":6,\n  \"fresh ginger\":4,\n  \"kale\":2,\n  \"garlic powder\":5,\n  \"dried thyme\":24,\n  \"salt\":214,\n  \"fresh basil\":20,\n  \"bean sprouts\":2,\n  \"shiitake mushrooms\":3,\n  \"potatoes\":2,\n  \"celery seeds\":2,\n  \"allspice\":5,\n  \"dijon mustard\":56,\n  \"soy sauce\":33,\n  \"broccoli\":2,\n  \"yellow mustard\":2,\n  \"maple syrup\":4,\n  \"sweet potatoes\":5,\n  \"egg whites\":4,\n  \"worcestershire sauce\":11,\n  \"brown sugar\":27,\n  \"lime juice\":5,\n  \"dry mustard\":7,\n  \"blueberries\":2,\n  \"mayonnaise\":3,\n  \"chicken wings\":3,\n  \"olive oil\":281,\n  \"vegetable oil\":23,\n  \"fresh thyme\":10,\n  \"flour\":20,\n  \"curry powder\":5,\n  \"white wine\":11,\n  \"sherry\":3,\n  \"yam\":2,\n  \"chicken breast\":3,\n  \"sugar\":71},\n \"blueberries\":\n {\"sour cream\":14,\n  \"yogurt\":5,\n  \"cream cheese\":17,\n  \"honey\":17,\n  \"lemon juice\":45,\n  \"molasses\":9,\n  \"ground cinnamon\":14,\n  \"bread\":2,\n  \"raisins\":5,\n  \"yeast\":5,\n  \"egg yolks\":8,\n  \"baking powder\":118,\n  \"cornstarch\":35,\n  \"orange\":6,\n  \"cream of tartar\":2,\n  \"cinnamon\":43,\n  \"canned peaches\":2,\n  \"eggs\":68,\n  \"almonds\":2,\n  \"margarine\":18,\n  \"fresh ginger\":2,\n  \"butter\":61,\n  \"vanilla\":43,\n  \"orange juice\":20,\n  \"whole wheat flour\":11,\n  \"salt\":113,\n  \"lemon\":5,\n  \"potatoes\":2,\n  \"buttermilk\":19,\n  \"powdered sugar\":7,\n  \"almond extract\":3,\n  \"cornmeal\":4,\n  \"oil\":11,\n  \"allspice\":3,\n  \"canola oil\":7,\n  \"strawberries\":38,\n  \"apples\":7,\n  \"maple syrup\":7,\n  \"walnuts\":3,\n  \"nutmeg\":16,\n  \"egg whites\":20,\n  \"baking soda\":51,\n  \"corn syrup\":4,\n  \"brown sugar\":13,\n  \"ginger\":2,\n  \"balsamic vinegar\":2,\n  \"lime juice\":5,\n  \"lime\":2,\n  \"white flour\":2,\n  \"pecans\":5,\n  \"olive oil\":2,\n  \"vegetable oil\":20,\n  \"banana\":13,\n  \"skim milk\":18,\n  \"flour\":86,\n  \"vinegar\":2,\n  \"shortening\":11,\n  \"applesauce\":5,\n  \"white wine\":2,\n  \"oats\":4,\n  \"whipping cream\":3,\n  \"sugar\":212},\n \"shell macaroni\":\n {\"tabasco sauce\":3,\n  \"ground beef\":5,\n  \"avocado\":2,\n  \"cheddar cheese\":3,\n  \"parsley\":3,\n  \"tomatoes\":4,\n  \"mayonnaise\":2,\n  \"lemon juice\":2,\n  \"oil\":3,\n  \"flour\":2,\n  \"butter\":2,\n  \"garlic powder\":4,\n  \"celery\":4,\n  \"carrots\":3,\n  \"green pepper\":2,\n  \"salt\":2,\n  \"bread crumbs\":2,\n  \"mushrooms\":2},\n \"sriracha hot chili sauce\":{\"black beans\":2, \"oil\":2},\n \"canned beef broth\":\n {\"tumeric\":2,\n  \"sour cream\":3,\n  \"bacon\":2,\n  \"parsley\":2,\n  \"cayenne pepper\":3,\n  \"lemon juice\":2,\n  \"honey\":2,\n  \"chili powder\":4,\n  \"paprika\":3,\n  \"soy sauce\":5,\n  \"fresh cilantro\":2,\n  \"cornstarch\":4,\n  \"brown sugar\":2,\n  \"ginger\":3,\n  \"ground beef\":2,\n  \"cheddar cheese\":2,\n  \"cumin\":3,\n  \"white pepper\":2,\n  \"vegetable oil\":5,\n  \"olive oil\":6,\n  \"flour\":2,\n  \"butter\":2,\n  \"garlic powder\":2,\n  \"dried thyme\":3,\n  \"parmesan cheese\":2,\n  \"lemon\":3,\n  \"salt\":7,\n  \"mushrooms\":2},\n \"fresh ginger\":\n {\"red wine\":2,\n  \"brown onions\":2,\n  \"cayenne pepper\":20,\n  \"honey\":15,\n  \"lemon juice\":34,\n  \"paprika\":11,\n  \"ground cinnamon\":10,\n  \"yeast\":2,\n  \"egg yolks\":3,\n  \"green pepper\":6,\n  \"baking powder\":3,\n  \"saffron\":4,\n  \"sesame oil\":47,\n  \"cream of tartar\":2,\n  \"cinnamon\":8,\n  \"pine nuts\":4,\n  \"tomatoes\":16,\n  \"white pepper\":9,\n  \"brown rice\":2,\n  \"black beans\":3,\n  \"fresh garlic\":7,\n  \"cauliflower\":2,\n  \"frozen peas\":2,\n  \"butter\":8,\n  \"salmon\":2,\n  \"orange juice\":7,\n  \"black pepper\":11,\n  \"lemon\":10,\n  \"tumeric\":3,\n  \"black olives\":2,\n  \"buttermilk\":4,\n  \"dried oregano\":3,\n  \"eggplant\":3,\n  \"hoisin sauce\":10,\n  \"cloves\":8,\n  \"oil\":28,\n  \"chicken\":8,\n  \"cucumber\":6,\n  \"canola oil\":8,\n  \"celery\":5,\n  \"walnuts\":3,\n  \"fresh peaches\":2,\n  \"fresh cilantro\":10,\n  \"nutmeg\":4,\n  \"baking soda\":6,\n  \"tabasco sauce\":3,\n  \"balsamic vinegar\":4,\n  \"lime\":5,\n  \"peanut butter\":6,\n  \"cumin\":7,\n  \"green onions\":34,\n  \"canned chicken broth\":2,\n  \"banana\":3,\n  \"beets\":2,\n  \"kidney beans\":2,\n  \"shortening\":2,\n  \"applesauce\":2,\n  \"parmesan cheese\":3,\n  \"carrots\":20,\n  \"mushrooms\":10,\n  \"sour cream\":3,\n  \"yogurt\":3,\n  \"tofu\":6,\n  \"cherry tomatoes\":4,\n  \"parsley\":6,\n  \"rice vinegar\":12,\n  \"molasses\":8,\n  \"chili powder\":7,\n  \"spaghetti\":2,\n  \"raisins\":5,\n  \"red wine vinegar\":8,\n  \"garam masala\":18,\n  \"chives\":2,\n  \"cornstarch\":52,\n  \"dried basil\":2,\n  \"orange\":2,\n  \"catsup\":2,\n  \"radishes\":3,\n  \"ground beef\":2,\n  \"sweet red pepper\":3,\n  \"cumin seeds\":10,\n  \"red pepper flakes\":4,\n  \"almonds\":4,\n  \"eggs\":5,\n  \"margarine\":2,\n  \"garlic powder\":6,\n  \"dried thyme\":3,\n  \"salt\":144,\n  \"fresh basil\":3,\n  \"bean sprouts\":7,\n  \"shiitake mushrooms\":3,\n  \"potatoes\":15,\n  \"frozen spinach\":4,\n  \"allspice\":3,\n  \"celery seeds\":2,\n  \"dijon mustard\":4,\n  \"soy sauce\":100,\n  \"broccoli\":3,\n  \"apples\":3,\n  \"maple syrup\":5,\n  \"egg whites\":5,\n  \"worcestershire sauce\":2,\n  \"brown sugar\":25,\n  \"lime juice\":17,\n  \"dry mustard\":3,\n  \"raw shrimp\":2,\n  \"blueberries\":2,\n  \"mayonnaise\":2,\n  \"chicken wings\":3,\n  \"olive oil\":22,\n  \"vegetable oil\":44,\n  \"curry powder\":12,\n  \"flour\":8,\n  \"vinegar\":7,\n  \"white wine\":3,\n  \"sherry\":17,\n  \"yam\":2,\n  \"whipping cream\":4,\n  \"chicken breast\":3,\n  \"sugar\":79},\n \"buttermilk\":\n {\"red wine\":3,\n  \"bacon\":5,\n  \"cayenne pepper\":9,\n  \"lemon juice\":34,\n  \"honey\":58,\n  \"paprika\":14,\n  \"ground cinnamon\":58,\n  \"yeast\":50,\n  \"egg yolks\":18,\n  \"green pepper\":3,\n  \"baking powder\":425,\n  \"cinnamon\":121,\n  \"pine nuts\":3,\n  \"cream of tartar\":14,\n  \"avocado\":2,\n  \"cheddar cheese\":13,\n  \"tomatoes\":3,\n  \"white pepper\":10,\n  \"brown rice\":2,\n  \"pinto beans\":2,\n  \"fresh garlic\":2,\n  \"frozen peas\":2,\n  \"butter\":284,\n  \"orange juice\":16,\n  \"lemon\":4,\n  \"black pepper\":18,\n  \"tumeric\":2,\n  \"powdered sugar\":21,\n  \"dried oregano\":4,\n  \"capers\":3,\n  \"cornmeal\":64,\n  \"cloves\":24,\n  \"oil\":58,\n  \"chicken\":5,\n  \"blue cheese\":8,\n  \"cucumber\":6,\n  \"flour tortilla\":2,\n  \"canola oil\":18,\n  \"strawberries\":8,\n  \"celery\":2,\n  \"fresh peaches\":2,\n  \"nutmeg\":55,\n  \"walnuts\":28,\n  \"fresh cilantro\":3,\n  \"baking soda\":635,\n  \"bread crumbs\":4,\n  \"tabasco sauce\":3,\n  \"corn syrup\":7,\n  \"balsamic vinegar\":2,\n  \"ginger\":24,\n  \"fresh rosemary\":2,\n  \"pecans\":38,\n  \"peanut butter\":2,\n  \"cumin\":2,\n  \"green onions\":10,\n  \"banana\":7,\n  \"skim milk\":5,\n  \"beets\":2,\n  \"barley\":3,\n  \"shortening\":100,\n  \"parmesan cheese\":8,\n  \"applesauce\":14,\n  \"carrots\":10,\n  \"mushrooms\":3,\n  \"sour cream\":22,\n  \"yogurt\":2,\n  \"cream cheese\":17,\n  \"cherry tomatoes\":2,\n  \"salsa\":2,\n  \"parsley\":11,\n  \"rice vinegar\":2,\n  \"molasses\":53,\n  \"chili powder\":9,\n  \"raisins\":81,\n  \"red wine vinegar\":5,\n  \"garam masala\":3,\n  \"chives\":5,\n  \"cornstarch\":16,\n  \"dried basil\":4,\n  \"orange\":10,\n  \"ground beef\":5,\n  \"sweet red pepper\":4,\n  \"almonds\":5,\n  \"eggs\":430,\n  \"margarine\":55,\n  \"fresh ginger\":4,\n  \"garlic powder\":15,\n  \"dried thyme\":6,\n  \"cream of mushroom soup\":3,\n  \"low fat milk\":2,\n  \"vanilla\":228,\n  \"whole wheat flour\":75,\n  \"salt\":829,\n  \"fresh basil\":4,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":5,\n  \"almond extract\":16,\n  \"frozen spinach\":2,\n  \"allspice\":29,\n  \"dijon mustard\":10,\n  \"soy sauce\":2,\n  \"apples\":7,\n  \"sweet potatoes\":5,\n  \"maple syrup\":15,\n  \"egg whites\":46,\n  \"worcestershire sauce\":10,\n  \"dried cilantro\":2,\n  \"brown sugar\":97,\n  \"lime juice\":5,\n  \"dry mustard\":5,\n  \"white flour\":9,\n  \"blueberries\":19,\n  \"mayonnaise\":21,\n  \"chicken wings\":3,\n  \"fresh thyme\":2,\n  \"vegetable oil\":97,\n  \"olive oil\":21,\n  \"flour\":391,\n  \"curry powder\":5,\n  \"vinegar\":14,\n  \"white wine\":3,\n  \"oats\":5,\n  \"whipping cream\":17,\n  \"sugar\":653},\n \"camembert cheese\":\n {\"worcestershire sauce\":2,\n  \"sour cream\":2,\n  \"cream cheese\":4,\n  \"dried oregano\":2,\n  \"mayonnaise\":2,\n  \"green onions\":2,\n  \"eggs\":2,\n  \"paprika\":3,\n  \"parmesan cheese\":2,\n  \"dried thyme\":2,\n  \"whipping cream\":2,\n  \"walnuts\":2,\n  \"salt\":2},\n \"canned clams\":\n {\"butter\":2, \"garlic powder\":2, \"potatoes\":2, \"sugar\":2, \"salt\":3},\n \"mushrooms\":\n {\"elbow macaroni\":5,\n  \"red wine\":42,\n  \"canned beef broth\":2,\n  \"canned tomato sauce\":4,\n  \"coconut oil\":2,\n  \"bacon\":54,\n  \"cayenne pepper\":29,\n  \"lemon juice\":136,\n  \"honey\":26,\n  \"swiss cheese\":22,\n  \"paprika\":87,\n  \"ground cinnamon\":4,\n  \"bread\":7,\n  \"dried bay leaf\":2,\n  \"yeast\":8,\n  \"egg yolks\":24,\n  \"green pepper\":122,\n  \"baking powder\":10,\n  \"saffron\":4,\n  \"sesame oil\":35,\n  \"cream of tartar\":2,\n  \"pine nuts\":11,\n  \"cinnamon\":9,\n  \"avocado\":3,\n  \"cheddar cheese\":45,\n  \"tomatoes\":122,\n  \"white pepper\":38,\n  \"brown rice\":19,\n  \"black beans\":4,\n  \"fresh garlic\":2,\n  \"pinto beans\":5,\n  \"cauliflower\":16,\n  \"frozen peas\":18,\n  \"butter\":440,\n  \"salmon\":4,\n  \"orange juice\":7,\n  \"black pepper\":108,\n  \"lemon\":29,\n  \"tumeric\":5,\n  \"black olives\":14,\n  \"buttermilk\":3,\n  \"hoisin sauce\":4,\n  \"eggplant\":25,\n  \"dried oregano\":28,\n  \"capers\":12,\n  \"cornmeal\":2,\n  \"cloves\":15,\n  \"oil\":162,\n  \"chicken\":59,\n  \"blue cheese\":4,\n  \"cucumber\":8,\n  \"canola oil\":17,\n  \"dried tarragon\":14,\n  \"celery\":118,\n  \"cornish game hens\":2,\n  \"alfalfa sprouts\":3,\n  \"fresh cilantro\":3,\n  \"walnuts\":13,\n  \"nutmeg\":55,\n  \"baking soda\":3,\n  \"bread crumbs\":58,\n  \"tabasco sauce\":8,\n  \"ginger\":35,\n  \"balsamic vinegar\":20,\n  \"fresh rosemary\":4,\n  \"lime\":2,\n  \"pecans\":9,\n  \"frozen corn\":3,\n  \"green onions\":109,\n  \"cumin\":25,\n  \"canned chicken broth\":2,\n  \"banana\":2,\n  \"beets\":2,\n  \"skim milk\":12,\n  \"barley\":10,\n  \"kidney beans\":7,\n  \"shortening\":7,\n  \"parmesan cheese\":112,\n  \"carrots\":139,\n  \"sour cream\":94,\n  \"yogurt\":8,\n  \"cream cheese\":21,\n  \"tofu\":27,\n  \"cherry tomatoes\":19,\n  \"salsa\":8,\n  \"parsley\":213,\n  \"green olives\":10,\n  \"rice vinegar\":8,\n  \"molasses\":6,\n  \"chili powder\":24,\n  \"beer\":12,\n  \"spaghetti\":18,\n  \"raisins\":7,\n  \"red wine vinegar\":15,\n  \"garam masala\":4,\n  \"chives\":22,\n  \"cornstarch\":111,\n  \"orange\":3,\n  \"dried basil\":37,\n  \"radishes\":4,\n  \"catsup\":11,\n  \"ground beef\":47,\n  \"romaine lettuce\":3,\n  \"mozzarella cheese\":40,\n  \"sweet red pepper\":14,\n  \"cumin seeds\":5,\n  \"barbecue sauce\":3,\n  \"red pepper flakes\":11,\n  \"almonds\":10,\n  \"eggs\":113,\n  \"margarine\":61,\n  \"fresh ginger\":10,\n  \"garlic powder\":55,\n  \"dried thyme\":42,\n  \"cream of mushroom soup\":30,\n  \"whole wheat flour\":8,\n  \"salt\":672,\n  \"fresh basil\":20,\n  \"bean sprouts\":30,\n  \"shiitake mushrooms\":4,\n  \"potatoes\":49,\n  \"frozen spinach\":6,\n  \"celery seeds\":2,\n  \"allspice\":5,\n  \"dijon mustard\":27,\n  \"soy sauce\":146,\n  \"broccoli\":33,\n  \"chicken legs\":4,\n  \"apples\":5,\n  \"shell macaroni\":2,\n  \"maple syrup\":2,\n  \"white rice\":5,\n  \"sweet potatoes\":2,\n  \"egg whites\":10,\n  \"worcestershire sauce\":82,\n  \"brown sugar\":16,\n  \"lime juice\":4,\n  \"dry mustard\":31,\n  \"raw shrimp\":2,\n  \"mayonnaise\":16,\n  \"olive oil\":272,\n  \"vegetable oil\":111,\n  \"fresh thyme\":10,\n  \"curry powder\":24,\n  \"flour\":300,\n  \"vinegar\":30,\n  \"white wine\":72,\n  \"sherry\":64,\n  \"yam\":2,\n  \"whipping cream\":24,\n  \"chicken breast\":14,\n  \"sugar\":96},\n \"honey\":\n {\"canned beef broth\":2,\n  \"elbow macaroni\":2,\n  \"red wine\":10,\n  \"canned tomato sauce\":2,\n  \"bacon\":8,\n  \"cayenne pepper\":52,\n  \"lemon juice\":295,\n  \"swiss cheese\":2,\n  \"zucchini squash\":2,\n  \"paprika\":52,\n  \"bread\":7,\n  \"ground cinnamon\":146,\n  \"yeast\":247,\n  \"egg yolks\":29,\n  \"green pepper\":22,\n  \"baking powder\":305,\n  \"saffron\":5,\n  \"sesame oil\":58,\n  \"pine nuts\":14,\n  \"cream of tartar\":16,\n  \"cinnamon\":336,\n  \"avocado\":4,\n  \"cheddar cheese\":7,\n  \"tomatoes\":19,\n  \"white pepper\":29,\n  \"brown rice\":12,\n  \"black beans\":11,\n  \"fresh garlic\":4,\n  \"cauliflower\":2,\n  \"frozen peas\":2,\n  \"butter\":365,\n  \"salmon\":4,\n  \"orange juice\":164,\n  \"lemon\":73,\n  \"black pepper\":72,\n  \"tumeric\":12,\n  \"black olives\":4,\n  \"buttermilk\":58,\n  \"powdered sugar\":17,\n  \"eggplant\":7,\n  \"dried oregano\":13,\n  \"hoisin sauce\":38,\n  \"capers\":4,\n  \"cornmeal\":44,\n  \"cloves\":60,\n  \"oil\":197,\n  \"chicken\":32,\n  \"blue cheese\":2,\n  \"cucumber\":6,\n  \"dry rosemary\":2,\n  \"canola oil\":58,\n  \"dried tarragon\":11,\n  \"strawberries\":30,\n  \"celery\":20,\n  \"alfalfa sprouts\":4,\n  \"cornish game hens\":7,\n  \"fresh peaches\":3,\n  \"fresh cilantro\":14,\n  \"nutmeg\":109,\n  \"walnuts\":61,\n  \"baking soda\":230,\n  \"bread crumbs\":8,\n  \"corn syrup\":5,\n  \"tabasco sauce\":21,\n  \"ginger\":114,\n  \"balsamic vinegar\":24,\n  \"fresh rosemary\":9,\n  \"lime\":13,\n  \"pecans\":24,\n  \"peanut butter\":55,\n  \"frozen corn\":2,\n  \"green onions\":51,\n  \"cumin\":25,\n  \"canned chicken broth\":3,\n  \"beets\":7,\n  \"skim milk\":33,\n  \"banana\":31,\n  \"barley\":3,\n  \"kidney beans\":3,\n  \"shortening\":52,\n  \"parmesan cheese\":7,\n  \"applesauce\":62,\n  \"carrots\":56,\n  \"mushrooms\":26,\n  \"sour cream\":40,\n  \"yogurt\":18,\n  \"brussels sprouts\":2,\n  \"cream cheese\":26,\n  \"tofu\":13,\n  \"cherry tomatoes\":10,\n  \"salsa\":3,\n  \"parsley\":27,\n  \"green olives\":2,\n  \"rice vinegar\":40,\n  \"molasses\":76,\n  \"chili powder\":44,\n  \"beer\":15,\n  \"spaghetti\":3,\n  \"raisins\":188,\n  \"red wine vinegar\":49,\n  \"garam masala\":2,\n  \"chives\":8,\n  \"cornstarch\":124,\n  \"dried basil\":12,\n  \"orange\":35,\n  \"catsup\":38,\n  \"ground beef\":4,\n  \"mozzarella cheese\":4,\n  \"adzuki beans\":2,\n  \"sweet red pepper\":3,\n  \"cumin seeds\":3,\n  \"barbecue sauce\":7,\n  \"red pepper flakes\":19,\n  \"eggs\":271,\n  \"almonds\":45,\n  \"margarine\":95,\n  \"fresh ginger\":15,\n  \"kale\":2,\n  \"garlic powder\":48,\n  \"dried thyme\":26,\n  \"low fat milk\":2,\n  \"vanilla\":208,\n  \"whole wheat flour\":246,\n  \"salt\":1244,\n  \"fresh basil\":6,\n  \"shiitake mushrooms\":5,\n  \"potatoes\":9,\n  \"almond extract\":33,\n  \"canned apricots\":2,\n  \"celery seeds\":5,\n  \"allspice\":54,\n  \"dijon mustard\":149,\n  \"soy sauce\":315,\n  \"broccoli\":8,\n  \"chicken legs\":3,\n  \"apples\":50,\n  \"yellow mustard\":5,\n  \"white rice\":2,\n  \"maple syrup\":20,\n  \"sweet potatoes\":16,\n  \"egg whites\":71,\n  \"worcestershire sauce\":65,\n  \"brown sugar\":160,\n  \"lime juice\":68,\n  \"dry mustard\":90,\n  \"white flour\":36,\n  \"blueberries\":17,\n  \"mayonnaise\":36,\n  \"chicken wings\":29,\n  \"fresh thyme\":12,\n  \"vegetable oil\":227,\n  \"olive oil\":210,\n  \"flour\":315,\n  \"curry powder\":55,\n  \"vinegar\":80,\n  \"white wine\":29,\n  \"sherry\":41,\n  \"oats\":37,\n  \"whipping cream\":19,\n  \"chicken breast\":8,\n  \"sugar\":409},\n \"alfalfa sprouts\":\n {\"sour cream\":5,\n  \"elbow macaroni\":2,\n  \"cream cheese\":3,\n  \"bacon\":3,\n  \"salsa\":3,\n  \"parsley\":4,\n  \"cayenne pepper\":2,\n  \"lemon juice\":7,\n  \"honey\":4,\n  \"rice vinegar\":3,\n  \"swiss cheese\":3,\n  \"chili powder\":2,\n  \"bread\":2,\n  \"yeast\":2,\n  \"red wine vinegar\":5,\n  \"egg yolks\":3,\n  \"green pepper\":3,\n  \"dried basil\":2,\n  \"orange\":2,\n  \"radishes\":3,\n  \"sesame oil\":3,\n  \"avocado\":6,\n  \"cheddar cheese\":5,\n  \"romaine lettuce\":3,\n  \"tomatoes\":5,\n  \"white pepper\":2,\n  \"brown rice\":2,\n  \"eggs\":5,\n  \"cauliflower\":2,\n  \"butter\":2,\n  \"garlic powder\":3,\n  \"orange juice\":2,\n  \"lemon\":3,\n  \"salt\":13,\n  \"bean sprouts\":2,\n  \"black olives\":2,\n  \"capers\":2,\n  \"oil\":2,\n  \"cucumber\":6,\n  \"canola oil\":2,\n  \"dijon mustard\":6,\n  \"dried tarragon\":2,\n  \"soy sauce\":4,\n  \"celery\":4,\n  \"broccoli\":2,\n  \"apples\":2,\n  \"walnuts\":2,\n  \"fresh cilantro\":2,\n  \"nutmeg\":2,\n  \"brown sugar\":2,\n  \"dry mustard\":2,\n  \"mayonnaise\":3,\n  \"green onions\":5,\n  \"cumin\":2,\n  \"olive oil\":9,\n  \"vegetable oil\":6,\n  \"skim milk\":2,\n  \"kidney beans\":2,\n  \"vinegar\":2,\n  \"carrots\":2,\n  \"sugar\":4,\n  \"mushrooms\":3},\n \"vanilla\":\n {\"sour cream\":302,\n  \"yogurt\":17,\n  \"red wine\":3,\n  \"tofu\":8,\n  \"cream cheese\":419,\n  \"cayenne pepper\":2,\n  \"lemon juice\":257,\n  \"honey\":208,\n  \"molasses\":47,\n  \"beer\":7,\n  \"bread\":21,\n  \"ground cinnamon\":141,\n  \"raisins\":381,\n  \"yeast\":32,\n  \"egg yolks\":320,\n  \"baking powder\":1555,\n  \"cornstarch\":262,\n  \"orange\":16,\n  \"pine nuts\":8,\n  \"cream of tartar\":208,\n  \"cinnamon\":842,\n  \"cheddar cheese\":2,\n  \"canned tuna\":2,\n  \"brown rice\":3,\n  \"eggs\":2594,\n  \"almonds\":82,\n  \"margarine\":426,\n  \"butter\":1845,\n  \"orange juice\":97,\n  \"whole wheat flour\":78,\n  \"black pepper\":9,\n  \"lemon\":33,\n  \"salt\":3155,\n  \"potatoes\":6,\n  \"buttermilk\":228,\n  \"powdered sugar\":430,\n  \"almond extract\":161,\n  \"cornmeal\":11,\n  \"cloves\":108,\n  \"oil\":258,\n  \"canned apricots\":2,\n  \"celery seeds\":2,\n  \"allspice\":73,\n  \"canola oil\":21,\n  \"strawberries\":53,\n  \"celery\":2,\n  \"apples\":113,\n  \"white rice\":2,\n  \"sweet potatoes\":8,\n  \"maple syrup\":44,\n  \"fresh peaches\":2,\n  \"walnuts\":229,\n  \"nutmeg\":325,\n  \"egg whites\":517,\n  \"bread crumbs\":3,\n  \"baking soda\":1393,\n  \"corn syrup\":124,\n  \"brown sugar\":926,\n  \"ginger\":57,\n  \"lime juice\":6,\n  \"lime\":3,\n  \"white flour\":18,\n  \"pecans\":244,\n  \"blueberries\":43,\n  \"mayonnaise\":10,\n  \"peanut butter\":195,\n  \"vegetable oil\":159,\n  \"beets\":4,\n  \"banana\":33,\n  \"skim milk\":108,\n  \"flour\":2196,\n  \"vinegar\":76,\n  \"shortening\":408,\n  \"parmesan cheese\":2,\n  \"white wine\":5,\n  \"applesauce\":87,\n  \"sherry\":5,\n  \"carrots\":34,\n  \"oats\":66,\n  \"whipping cream\":265,\n  \"sugar\":4410},\n \"sour cream\":\n {\"elbow macaroni\":3,\n  \"canned beef broth\":3,\n  \"red wine\":14,\n  \"canned tomato sauce\":3,\n  \"bacon\":76,\n  \"cayenne pepper\":42,\n  \"lemon juice\":248,\n  \"honey\":40,\n  \"swiss cheese\":14,\n  \"paprika\":179,\n  \"camembert cheese\":2,\n  \"bread\":6,\n  \"ground cinnamon\":46,\n  \"yeast\":19,\n  \"egg yolks\":64,\n  \"green pepper\":62,\n  \"baking powder\":188,\n  \"saffron\":5,\n  \"sesame oil\":3,\n  \"pine nuts\":4,\n  \"cream of tartar\":17,\n  \"cinnamon\":123,\n  \"avocado\":29,\n  \"cheddar cheese\":132,\n  \"corn tortilla\":4,\n  \"tomatoes\":87,\n  \"white pepper\":49,\n  \"brown rice\":3,\n  \"black beans\":15,\n  \"fresh garlic\":2,\n  \"pinto beans\":6,\n  \"cauliflower\":2,\n  \"frozen peas\":11,\n  \"butter\":675,\n  \"salmon\":9,\n  \"orange juice\":22,\n  \"lemon\":35,\n  \"black pepper\":80,\n  \"tumeric\":2,\n  \"black olives\":24,\n  \"buttermilk\":22,\n  \"powdered sugar\":52,\n  \"eggplant\":4,\n  \"dried oregano\":21,\n  \"capers\":14,\n  \"cornmeal\":16,\n  \"cloves\":25,\n  \"oil\":114,\n  \"chicken\":40,\n  \"blue cheese\":16,\n  \"cucumber\":35,\n  \"flour tortilla\":7,\n  \"canola oil\":10,\n  \"dried tarragon\":4,\n  \"strawberries\":15,\n  \"celery\":58,\n  \"alfalfa sprouts\":5,\n  \"fresh peaches\":4,\n  \"nutmeg\":69,\n  \"walnuts\":41,\n  \"fresh cilantro\":23,\n  \"bread crumbs\":40,\n  \"baking soda\":232,\n  \"corn syrup\":3,\n  \"tabasco sauce\":36,\n  \"balsamic vinegar\":6,\n  \"ginger\":23,\n  \"lime\":10,\n  \"pecans\":42,\n  \"peanut butter\":4,\n  \"frozen corn\":4,\n  \"green onions\":112,\n  \"cumin\":50,\n  \"canned chicken broth\":4,\n  \"skim milk\":4,\n  \"beets\":11,\n  \"banana\":17,\n  \"kidney beans\":10,\n  \"shortening\":40,\n  \"applesauce\":5,\n  \"parmesan cheese\":64,\n  \"carrots\":43,\n  \"mushrooms\":94,\n  \"yogurt\":10,\n  \"cream cheese\":274,\n  \"cherry tomatoes\":2,\n  \"salsa\":61,\n  \"parsley\":82,\n  \"green olives\":3,\n  \"rice vinegar\":4,\n  \"molasses\":12,\n  \"beer\":11,\n  \"chili powder\":74,\n  \"spaghetti\":5,\n  \"raisins\":51,\n  \"red wine vinegar\":19,\n  \"chives\":32,\n  \"cornstarch\":75,\n  \"dried basil\":9,\n  \"orange\":8,\n  \"radishes\":11,\n  \"catsup\":14,\n  \"ground beef\":89,\n  \"romaine lettuce\":2,\n  \"mozzarella cheese\":17,\n  \"sweet red pepper\":4,\n  \"cumin seeds\":5,\n  \"canned peaches\":2,\n  \"barbecue sauce\":2,\n  \"raw spinach\":2,\n  \"red pepper flakes\":6,\n  \"almonds\":22,\n  \"eggs\":564,\n  \"margarine\":106,\n  \"fresh ginger\":3,\n  \"garlic powder\":74,\n  \"kale\":3,\n  \"dried thyme\":6,\n  \"cream of mushroom soup\":51,\n  \"vanilla\":302,\n  \"whole wheat flour\":15,\n  \"salt\":1198,\n  \"fresh basil\":10,\n  \"bean sprouts\":2,\n  \"potatoes\":72,\n  \"almond extract\":38,\n  \"frozen spinach\":7,\n  \"canned apricots\":2,\n  \"celery seeds\":3,\n  \"allspice\":7,\n  \"dijon mustard\":50,\n  \"soy sauce\":19,\n  \"broccoli\":4,\n  \"apples\":17,\n  \"sweet potatoes\":2,\n  \"yellow mustard\":2,\n  \"maple syrup\":9,\n  \"egg whites\":23,\n  \"worcestershire sauce\":99,\n  \"dried cilantro\":2,\n  \"brown sugar\":84,\n  \"lime juice\":28,\n  \"dry mustard\":46,\n  \"raw shrimp\":2,\n  \"white flour\":6,\n  \"blueberries\":14,\n  \"mayonnaise\":235,\n  \"chicken wings\":6,\n  \"fresh thyme\":6,\n  \"olive oil\":82,\n  \"vegetable oil\":117,\n  \"flour\":522,\n  \"curry powder\":54,\n  \"vinegar\":55,\n  \"white wine\":25,\n  \"sherry\":22,\n  \"whipping cream\":45,\n  \"chicken breast\":13,\n  \"sugar\":809},\n \"tabasco sauce\":\n {\"red wine\":3,\n  \"elbow macaroni\":3,\n  \"canned tomato sauce\":4,\n  \"bacon\":16,\n  \"cayenne pepper\":48,\n  \"honey\":21,\n  \"lemon juice\":92,\n  \"paprika\":61,\n  \"ground cinnamon\":5,\n  \"bread\":4,\n  \"yeast\":3,\n  \"egg yolks\":5,\n  \"green pepper\":45,\n  \"baking powder\":6,\n  \"saffron\":4,\n  \"sesame oil\":10,\n  \"pine nuts\":2,\n  \"cinnamon\":7,\n  \"avocado\":3,\n  \"cheddar cheese\":12,\n  \"tomatoes\":50,\n  \"white pepper\":26,\n  \"brown rice\":12,\n  \"black beans\":7,\n  \"fresh garlic\":2,\n  \"pinto beans\":7,\n  \"frozen peas\":2,\n  \"butter\":118,\n  \"salmon\":4,\n  \"orange juice\":7,\n  \"lemon\":20,\n  \"black pepper\":78,\n  \"black olives\":7,\n  \"buttermilk\":3,\n  \"hoisin sauce\":2,\n  \"dried oregano\":10,\n  \"eggplant\":3,\n  \"capers\":9,\n  \"cornmeal\":2,\n  \"cloves\":3,\n  \"oil\":32,\n  \"chicken\":4,\n  \"blue cheese\":3,\n  \"cucumber\":16,\n  \"canola oil\":4,\n  \"dried tarragon\":3,\n  \"celery\":36,\n  \"walnuts\":2,\n  \"fresh cilantro\":7,\n  \"nutmeg\":5,\n  \"bread crumbs\":14,\n  \"balsamic vinegar\":4,\n  \"ginger\":8,\n  \"lime\":5,\n  \"pecans\":4,\n  \"peanut butter\":2,\n  \"frozen corn\":2,\n  \"green onions\":29,\n  \"cumin\":45,\n  \"skim milk\":2,\n  \"barley\":2,\n  \"kidney beans\":6,\n  \"shortening\":3,\n  \"parmesan cheese\":16,\n  \"carrots\":17,\n  \"mushrooms\":8,\n  \"sour cream\":36,\n  \"yogurt\":2,\n  \"brussels sprouts\":2,\n  \"cream cheese\":24,\n  \"cherry tomatoes\":3,\n  \"salsa\":3,\n  \"parsley\":41,\n  \"green olives\":2,\n  \"rice vinegar\":4,\n  \"molasses\":19,\n  \"beer\":22,\n  \"chili powder\":77,\n  \"spaghetti\":2,\n  \"raisins\":5,\n  \"red wine vinegar\":21,\n  \"chives\":6,\n  \"cornstarch\":12,\n  \"orange\":4,\n  \"dried basil\":9,\n  \"catsup\":20,\n  \"ground beef\":18,\n  \"mozzarella cheese\":5,\n  \"cumin seeds\":3,\n  \"barbecue sauce\":2,\n  \"red pepper flakes\":11,\n  \"eggs\":41,\n  \"margarine\":19,\n  \"fresh ginger\":3,\n  \"kale\":2,\n  \"garlic powder\":53,\n  \"dried thyme\":13,\n  \"cream of mushroom soup\":2,\n  \"low fat milk\":2,\n  \"whole wheat flour\":2,\n  \"salt\":272,\n  \"fresh basil\":2,\n  \"potatoes\":6,\n  \"allspice\":11,\n  \"celery seeds\":2,\n  \"dijon mustard\":20,\n  \"soy sauce\":56,\n  \"apples\":3,\n  \"sweet potatoes\":2,\n  \"shell macaroni\":3,\n  \"maple syrup\":7,\n  \"yellow mustard\":3,\n  \"white rice\":3,\n  \"egg whites\":2,\n  \"worcestershire sauce\":181,\n  \"brown sugar\":44,\n  \"lime juice\":16,\n  \"dry mustard\":51,\n  \"raw shrimp\":2,\n  \"white flour\":2,\n  \"mayonnaise\":43,\n  \"chicken wings\":15,\n  \"olive oil\":72,\n  \"vegetable oil\":38,\n  \"flour\":61,\n  \"curry powder\":11,\n  \"vinegar\":45,\n  \"white wine\":10,\n  \"sherry\":8,\n  \"whipping cream\":4,\n  \"chicken breast\":4,\n  \"sugar\":84},\n \"canned peaches\":\n {\"baking powder\":2,\n  \"sour cream\":2,\n  \"brown sugar\":2,\n  \"cinnamon\":3,\n  \"lime juice\":3,\n  \"blueberries\":2,\n  \"oil\":2,\n  \"curry powder\":2,\n  \"flour\":2,\n  \"butter\":2,\n  \"garlic powder\":2,\n  \"celery\":2,\n  \"nutmeg\":2,\n  \"salt\":3,\n  \"sugar\":4},\n \"maple syrup\":\n {\"red wine\":3,\n  \"bacon\":5,\n  \"cayenne pepper\":2,\n  \"lemon juice\":37,\n  \"honey\":20,\n  \"swiss cheese\":2,\n  \"paprika\":4,\n  \"bread\":7,\n  \"ground cinnamon\":34,\n  \"yeast\":22,\n  \"egg yolks\":15,\n  \"green pepper\":3,\n  \"baking powder\":76,\n  \"sesame oil\":3,\n  \"pine nuts\":2,\n  \"cinnamon\":64,\n  \"cream of tartar\":3,\n  \"tomatoes\":3,\n  \"brown rice\":2,\n  \"pinto beans\":3,\n  \"butter\":98,\n  \"orange juice\":18,\n  \"black pepper\":12,\n  \"lemon\":3,\n  \"black olives\":3,\n  \"buttermilk\":15,\n  \"powdered sugar\":13,\n  \"hoisin sauce\":3,\n  \"cornmeal\":8,\n  \"cloves\":11,\n  \"oil\":18,\n  \"chicken\":3,\n  \"cucumber\":3,\n  \"canola oil\":11,\n  \"strawberries\":3,\n  \"cornish game hens\":3,\n  \"fresh cilantro\":2,\n  \"fresh peaches\":4,\n  \"walnuts\":9,\n  \"nutmeg\":40,\n  \"baking soda\":47,\n  \"bread crumbs\":2,\n  \"tabasco sauce\":7,\n  \"corn syrup\":2,\n  \"ginger\":20,\n  \"balsamic vinegar\":4,\n  \"fresh rosemary\":2,\n  \"pecans\":14,\n  \"peanut butter\":6,\n  \"frozen corn\":2,\n  \"cumin\":4,\n  \"green onions\":3,\n  \"skim milk\":15,\n  \"banana\":3,\n  \"barley\":2,\n  \"shortening\":8,\n  \"parmesan cheese\":2,\n  \"applesauce\":10,\n  \"carrots\":9,\n  \"mushrooms\":2,\n  \"sour cream\":9,\n  \"yogurt\":2,\n  \"tofu\":4,\n  \"cream cheese\":4,\n  \"parsley\":2,\n  \"rice vinegar\":2,\n  \"molasses\":15,\n  \"chili powder\":7,\n  \"beer\":3,\n  \"raisins\":30,\n  \"red wine vinegar\":3,\n  \"cornstarch\":19,\n  \"orange\":5,\n  \"catsup\":4,\n  \"ground beef\":2,\n  \"cumin seeds\":2,\n  \"red pepper flakes\":5,\n  \"almonds\":6,\n  \"eggs\":72,\n  \"margarine\":22,\n  \"fresh ginger\":5,\n  \"garlic powder\":3,\n  \"vanilla\":44,\n  \"whole wheat flour\":17,\n  \"salt\":182,\n  \"fresh basil\":2,\n  \"potatoes\":2,\n  \"almond extract\":5,\n  \"allspice\":9,\n  \"dijon mustard\":15,\n  \"soy sauce\":17,\n  \"apples\":12,\n  \"yellow mustard\":2,\n  \"sweet potatoes\":8,\n  \"egg whites\":36,\n  \"worcestershire sauce\":12,\n  \"brown sugar\":37,\n  \"lime juice\":10,\n  \"dry mustard\":17,\n  \"white flour\":3,\n  \"blueberries\":7,\n  \"mayonnaise\":2,\n  \"chicken wings\":2,\n  \"olive oil\":14,\n  \"vegetable oil\":14,\n  \"flour\":60,\n  \"curry powder\":2,\n  \"vinegar\":6,\n  \"white wine\":10,\n  \"sherry\":6,\n  \"oats\":4,\n  \"whipping cream\":12,\n  \"chicken breast\":2,\n  \"sugar\":74},\n \"fresh rosemary\":\n {\"red wine\":3,\n  \"cream cheese\":2,\n  \"cherry tomatoes\":2,\n  \"canned tomato sauce\":2,\n  \"bacon\":4,\n  \"parsley\":4,\n  \"cayenne pepper\":3,\n  \"lemon juice\":11,\n  \"honey\":9,\n  \"molasses\":2,\n  \"paprika\":3,\n  \"spaghetti\":2,\n  \"ground cinnamon\":2,\n  \"bread\":2,\n  \"raisins\":2,\n  \"yeast\":2,\n  \"red wine vinegar\":6,\n  \"chives\":2,\n  \"cornstarch\":4,\n  \"orange\":2,\n  \"dried basil\":3,\n  \"pine nuts\":2,\n  \"tomatoes\":6,\n  \"sweet red pepper\":2,\n  \"white pepper\":2,\n  \"red pepper flakes\":2,\n  \"almonds\":2,\n  \"eggs\":5,\n  \"fresh garlic\":2,\n  \"margarine\":3,\n  \"butter\":17,\n  \"dried thyme\":6,\n  \"orange juice\":7,\n  \"whole wheat flour\":2,\n  \"lemon\":2,\n  \"black pepper\":5,\n  \"salt\":50,\n  \"fresh basil\":11,\n  \"black olives\":3,\n  \"shiitake mushrooms\":2,\n  \"buttermilk\":2,\n  \"potatoes\":6,\n  \"dried oregano\":2,\n  \"cornmeal\":3,\n  \"cloves\":2,\n  \"oil\":5,\n  \"chicken\":6,\n  \"canola oil\":8,\n  \"dijon mustard\":8,\n  \"celery\":2,\n  \"maple syrup\":2,\n  \"walnuts\":3,\n  \"nutmeg\":2,\n  \"baking soda\":2,\n  \"bread crumbs\":2,\n  \"worcestershire sauce\":2,\n  \"balsamic vinegar\":5,\n  \"lime juice\":2,\n  \"dry mustard\":2,\n  \"raw shrimp\":2,\n  \"white flour\":2,\n  \"pecans\":3,\n  \"green onions\":2,\n  \"vegetable oil\":5,\n  \"fresh thyme\":14,\n  \"olive oil\":67,\n  \"barley\":2,\n  \"curry powder\":3,\n  \"flour\":4,\n  \"vinegar\":2,\n  \"parmesan cheese\":8,\n  \"white wine\":9,\n  \"carrots\":7,\n  \"whipping cream\":2,\n  \"sugar\":8,\n  \"mushrooms\":4},\n \"ground beef\":\n {\"canned beef broth\":2,\n  \"elbow macaroni\":12,\n  \"red wine\":12,\n  \"canned tomato sauce\":2,\n  \"bacon\":34,\n  \"cayenne pepper\":38,\n  \"honey\":4,\n  \"lemon juice\":29,\n  \"swiss cheese\":7,\n  \"paprika\":70,\n  \"ground cinnamon\":9,\n  \"bread\":18,\n  \"egg yolks\":9,\n  \"green pepper\":117,\n  \"baking powder\":8,\n  \"sesame oil\":9,\n  \"cinnamon\":35,\n  \"pine nuts\":6,\n  \"avocado\":8,\n  \"cheddar cheese\":97,\n  \"corn tortilla\":2,\n  \"tomatoes\":133,\n  \"white pepper\":14,\n  \"brown rice\":4,\n  \"black beans\":3,\n  \"pinto beans\":10,\n  \"frozen peas\":3,\n  \"butter\":96,\n  \"orange juice\":2,\n  \"black pepper\":74,\n  \"lemon\":6,\n  \"black olives\":5,\n  \"buttermilk\":5,\n  \"hoisin sauce\":3,\n  \"eggplant\":14,\n  \"dried oregano\":37,\n  \"capers\":11,\n  \"cornmeal\":13,\n  \"cloves\":13,\n  \"oil\":66,\n  \"chicken\":5,\n  \"blue cheese\":2,\n  \"cucumber\":3,\n  \"canola oil\":4,\n  \"celery\":49,\n  \"walnuts\":2,\n  \"fresh cilantro\":6,\n  \"nutmeg\":34,\n  \"baking soda\":2,\n  \"bread crumbs\":76,\n  \"tabasco sauce\":18,\n  \"ginger\":10,\n  \"pecans\":2,\n  \"peanut butter\":2,\n  \"frozen corn\":3,\n  \"cumin\":68,\n  \"green onions\":23,\n  \"skim milk\":3,\n  \"beets\":2,\n  \"barley\":3,\n  \"kidney beans\":45,\n  \"shortening\":25,\n  \"parmesan cheese\":72,\n  \"applesauce\":4,\n  \"carrots\":31,\n  \"mushrooms\":47,\n  \"sour cream\":89,\n  \"yogurt\":2,\n  \"cream cheese\":9,\n  \"cherry tomatoes\":2,\n  \"salsa\":16,\n  \"parsley\":65,\n  \"green olives\":9,\n  \"molasses\":6,\n  \"beer\":9,\n  \"chili powder\":250,\n  \"spaghetti\":17,\n  \"raisins\":15,\n  \"red wine vinegar\":5,\n  \"garam masala\":3,\n  \"chives\":4,\n  \"cornstarch\":31,\n  \"dried basil\":29,\n  \"catsup\":68,\n  \"mozzarella cheese\":50,\n  \"sweet red pepper\":3,\n  \"cumin seeds\":2,\n  \"barbecue sauce\":10,\n  \"red pepper flakes\":7,\n  \"eggs\":169,\n  \"almonds\":4,\n  \"margarine\":13,\n  \"fresh ginger\":2,\n  \"garlic powder\":93,\n  \"dried thyme\":8,\n  \"cream of mushroom soup\":46,\n  \"whole wheat flour\":2,\n  \"salt\":712,\n  \"fresh basil\":4,\n  \"bean sprouts\":9,\n  \"potatoes\":42,\n  \"frozen spinach\":4,\n  \"celery seeds\":2,\n  \"allspice\":23,\n  \"dijon mustard\":6,\n  \"soy sauce\":44,\n  \"apples\":6,\n  \"sweet potatoes\":2,\n  \"shell macaroni\":5,\n  \"maple syrup\":2,\n  \"white rice\":2,\n  \"yellow mustard\":3,\n  \"egg whites\":2,\n  \"worcestershire sauce\":113,\n  \"dried cilantro\":2,\n  \"brown sugar\":75,\n  \"lime juice\":2,\n  \"dry mustard\":32,\n  \"mayonnaise\":14,\n  \"chicken wings\":4,\n  \"vegetable oil\":59,\n  \"olive oil\":57,\n  \"flour\":92,\n  \"curry powder\":11,\n  \"vinegar\":59,\n  \"white wine\":8,\n  \"sherry\":4,\n  \"oats\":4,\n  \"yam\":2,\n  \"whipping cream\":3,\n  \"sugar\":102},\n \"blue cheese\":\n {\"sour cream\":16,\n  \"yogurt\":3,\n  \"cream cheese\":26,\n  \"bacon\":6,\n  \"parsley\":9,\n  \"cayenne pepper\":4,\n  \"green olives\":2,\n  \"honey\":2,\n  \"lemon juice\":14,\n  \"rice vinegar\":3,\n  \"swiss cheese\":5,\n  \"chili powder\":4,\n  \"paprika\":10,\n  \"red wine vinegar\":5,\n  \"yeast\":5,\n  \"chives\":6,\n  \"baking powder\":2,\n  \"cornstarch\":2,\n  \"catsup\":4,\n  \"sesame oil\":2,\n  \"ground beef\":2,\n  \"avocado\":3,\n  \"cheddar cheese\":2,\n  \"romaine lettuce\":4,\n  \"tomatoes\":10,\n  \"cumin seeds\":3,\n  \"white pepper\":6,\n  \"almonds\":2,\n  \"eggs\":4,\n  \"butter\":19,\n  \"garlic powder\":4,\n  \"orange juice\":2,\n  \"black pepper\":9,\n  \"salt\":38,\n  \"lemon\":2,\n  \"fresh basil\":2,\n  \"black olives\":4,\n  \"buttermilk\":8,\n  \"potatoes\":5,\n  \"dried oregano\":2,\n  \"capers\":2,\n  \"oil\":5,\n  \"cucumber\":3,\n  \"dijon mustard\":5,\n  \"soy sauce\":4,\n  \"celery\":4,\n  \"broccoli\":2,\n  \"walnuts\":6,\n  \"nutmeg\":3,\n  \"bread crumbs\":2,\n  \"worcestershire sauce\":12,\n  \"tabasco sauce\":3,\n  \"balsamic vinegar\":4,\n  \"dry mustard\":5,\n  \"mayonnaise\":16,\n  \"green onions\":3,\n  \"chicken wings\":5,\n  \"olive oil\":13,\n  \"vegetable oil\":4,\n  \"banana\":2,\n  \"curry powder\":2,\n  \"flour\":6,\n  \"vinegar\":6,\n  \"white wine\":2,\n  \"applesauce\":2,\n  \"parmesan cheese\":3,\n  \"carrots\":3,\n  \"sherry\":2,\n  \"chicken breast\":2,\n  \"mushrooms\":4,\n  \"sugar\":12},\n \"chicken wings\":\n {\"sour cream\":6,\n  \"bacon\":2,\n  \"cayenne pepper\":7,\n  \"parsley\":4,\n  \"lemon juice\":18,\n  \"honey\":29,\n  \"molasses\":5,\n  \"chili powder\":10,\n  \"paprika\":12,\n  \"beer\":5,\n  \"ground cinnamon\":3,\n  \"red wine vinegar\":3,\n  \"green pepper\":2,\n  \"baking powder\":2,\n  \"cornstarch\":17,\n  \"catsup\":13,\n  \"sesame oil\":6,\n  \"cinnamon\":2,\n  \"ground beef\":4,\n  \"tomatoes\":5,\n  \"barbecue sauce\":4,\n  \"white pepper\":2,\n  \"red pepper flakes\":4,\n  \"eggs\":7,\n  \"almonds\":2,\n  \"margarine\":3,\n  \"fresh ginger\":3,\n  \"frozen peas\":2,\n  \"butter\":26,\n  \"garlic powder\":18,\n  \"dried thyme\":3,\n  \"orange juice\":4,\n  \"lemon\":4,\n  \"salt\":57,\n  \"black pepper\":17,\n  \"tumeric\":2,\n  \"buttermilk\":3,\n  \"dried oregano\":3,\n  \"hoisin sauce\":3,\n  \"cloves\":2,\n  \"oil\":16,\n  \"chicken\":2,\n  \"blue cheese\":5,\n  \"cucumber\":2,\n  \"canola oil\":2,\n  \"celery seeds\":2,\n  \"allspice\":4,\n  \"dijon mustard\":5,\n  \"soy sauce\":52,\n  \"celery\":5,\n  \"apples\":2,\n  \"maple syrup\":2,\n  \"nutmeg\":2,\n  \"egg whites\":3,\n  \"worcestershire sauce\":16,\n  \"corn syrup\":2,\n  \"tabasco sauce\":15,\n  \"brown sugar\":26,\n  \"balsamic vinegar\":3,\n  \"ginger\":6,\n  \"lime juice\":8,\n  \"dry mustard\":13,\n  \"peanut butter\":3,\n  \"mayonnaise\":5,\n  \"cumin\":5,\n  \"green onions\":5,\n  \"olive oil\":5,\n  \"vegetable oil\":27,\n  \"curry powder\":4,\n  \"flour\":15,\n  \"vinegar\":16,\n  \"parmesan cheese\":3,\n  \"white wine\":3,\n  \"sherry\":5,\n  \"carrots\":3,\n  \"sugar\":23},\n \"shortening\":\n {\"red wine\":2,\n  \"bacon\":5,\n  \"cayenne pepper\":5,\n  \"lemon juice\":49,\n  \"honey\":52,\n  \"swiss cheese\":2,\n  \"zucchini squash\":2,\n  \"paprika\":30,\n  \"ground cinnamon\":47,\n  \"bread\":3,\n  \"yeast\":59,\n  \"egg yolks\":21,\n  \"green pepper\":10,\n  \"baking powder\":675,\n  \"saffron\":2,\n  \"sesame oil\":2,\n  \"pine nuts\":3,\n  \"cream of tartar\":46,\n  \"cinnamon\":300,\n  \"cheddar cheese\":10,\n  \"tomatoes\":15,\n  \"white pepper\":3,\n  \"pinto beans\":3,\n  \"butter\":190,\n  \"frozen peas\":5,\n  \"salmon\":2,\n  \"orange juice\":27,\n  \"lemon\":10,\n  \"black pepper\":17,\n  \"black olives\":3,\n  \"buttermilk\":100,\n  \"powdered sugar\":53,\n  \"cornmeal\":32,\n  \"cloves\":90,\n  \"oil\":13,\n  \"chicken\":8,\n  \"strawberries\":6,\n  \"celery\":11,\n  \"walnuts\":50,\n  \"nutmeg\":126,\n  \"fresh cilantro\":2,\n  \"baking soda\":481,\n  \"bread crumbs\":8,\n  \"tabasco sauce\":3,\n  \"corn syrup\":13,\n  \"ginger\":74,\n  \"pecans\":34,\n  \"peanut butter\":46,\n  \"cumin\":6,\n  \"green onions\":4,\n  \"skim milk\":3,\n  \"banana\":4,\n  \"barley\":2,\n  \"parmesan cheese\":6,\n  \"applesauce\":18,\n  \"carrots\":13,\n  \"mushrooms\":7,\n  \"sour cream\":40,\n  \"yogurt\":2,\n  \"tofu\":2,\n  \"cream cheese\":16,\n  \"salsa\":2,\n  \"parsley\":10,\n  \"molasses\":99,\n  \"chili powder\":14,\n  \"beer\":5,\n  \"spaghetti\":2,\n  \"raisins\":137,\n  \"red wine vinegar\":3,\n  \"cornstarch\":23,\n  \"orange\":6,\n  \"dried basil\":2,\n  \"catsup\":10,\n  \"ground beef\":25,\n  \"mozzarella cheese\":2,\n  \"cumin seeds\":2,\n  \"barbecue sauce\":2,\n  \"eggs\":538,\n  \"almonds\":10,\n  \"margarine\":36,\n  \"fresh ginger\":2,\n  \"garlic powder\":8,\n  \"cream of mushroom soup\":6,\n  \"vanilla\":408,\n  \"whole wheat flour\":28,\n  \"salt\":1377,\n  \"bean sprouts\":2,\n  \"potatoes\":13,\n  \"almond extract\":37,\n  \"allspice\":44,\n  \"soy sauce\":8,\n  \"chicken legs\":2,\n  \"apples\":25,\n  \"maple syrup\":8,\n  \"sweet potatoes\":5,\n  \"egg whites\":35,\n  \"worcestershire sauce\":18,\n  \"brown sugar\":286,\n  \"lime juice\":4,\n  \"dry mustard\":12,\n  \"white flour\":17,\n  \"blueberries\":11,\n  \"mayonnaise\":3,\n  \"olive oil\":2,\n  \"vegetable oil\":11,\n  \"flour\":867,\n  \"curry powder\":3,\n  \"vinegar\":42,\n  \"white wine\":5,\n  \"sherry\":3,\n  \"oats\":24,\n  \"whipping cream\":9,\n  \"chicken breast\":2,\n  \"sugar\":1042},\n \"cloves\":\n {\"red wine\":20,\n  \"bacon\":13,\n  \"brown onions\":2,\n  \"cayenne pepper\":23,\n  \"lemon juice\":62,\n  \"honey\":60,\n  \"swiss cheese\":2,\n  \"paprika\":49,\n  \"ground cinnamon\":23,\n  \"yeast\":11,\n  \"egg yolks\":16,\n  \"green pepper\":15,\n  \"baking powder\":179,\n  \"saffron\":12,\n  \"sesame oil\":3,\n  \"cream of tartar\":2,\n  \"cinnamon\":583,\n  \"pine nuts\":5,\n  \"avocado\":3,\n  \"cheddar cheese\":3,\n  \"tomatoes\":41,\n  \"white pepper\":12,\n  \"brown rice\":5,\n  \"black beans\":5,\n  \"pinto beans\":5,\n  \"cauliflower\":2,\n  \"frozen peas\":2,\n  \"butter\":198,\n  \"salmon\":3,\n  \"orange juice\":30,\n  \"black pepper\":44,\n  \"lemon\":34,\n  \"tumeric\":10,\n  \"black olives\":3,\n  \"buttermilk\":24,\n  \"powdered sugar\":13,\n  \"dried oregano\":9,\n  \"eggplant\":8,\n  \"capers\":6,\n  \"cornmeal\":2,\n  \"oil\":75,\n  \"chicken\":29,\n  \"cucumber\":2,\n  \"canola oil\":5,\n  \"strawberries\":3,\n  \"celery\":13,\n  \"cornish game hens\":2,\n  \"fresh peaches\":2,\n  \"nutmeg\":309,\n  \"walnuts\":35,\n  \"bread crumbs\":14,\n  \"baking soda\":209,\n  \"tabasco sauce\":3,\n  \"corn syrup\":8,\n  \"balsamic vinegar\":5,\n  \"ginger\":196,\n  \"fresh rosemary\":2,\n  \"lime\":11,\n  \"pecans\":24,\n  \"peanut butter\":5,\n  \"frozen corn\":2,\n  \"cumin\":49,\n  \"green onions\":6,\n  \"beets\":6,\n  \"skim milk\":10,\n  \"banana\":3,\n  \"kidney beans\":4,\n  \"shortening\":90,\n  \"parmesan cheese\":3,\n  \"applesauce\":42,\n  \"carrots\":44,\n  \"mushrooms\":15,\n  \"sour cream\":25,\n  \"yogurt\":19,\n  \"tofu\":6,\n  \"cream cheese\":8,\n  \"cherry tomatoes\":2,\n  \"salsa\":2,\n  \"parsley\":31,\n  \"green olives\":3,\n  \"rice vinegar\":3,\n  \"molasses\":78,\n  \"chili powder\":34,\n  \"beer\":8,\n  \"spaghetti\":2,\n  \"raisins\":184,\n  \"red wine vinegar\":18,\n  \"chives\":6,\n  \"garam masala\":8,\n  \"cornstarch\":26,\n  \"dried basil\":5,\n  \"orange\":22,\n  \"catsup\":5,\n  \"ground beef\":13,\n  \"mozzarella cheese\":2,\n  \"sweet red pepper\":2,\n  \"cumin seeds\":37,\n  \"red pepper flakes\":4,\n  \"eggs\":202,\n  \"almonds\":33,\n  \"margarine\":37,\n  \"fresh ginger\":8,\n  \"garlic powder\":8,\n  \"dried thyme\":10,\n  \"vanilla\":108,\n  \"whole wheat flour\":16,\n  \"salt\":611,\n  \"fresh basil\":2,\n  \"potatoes\":15,\n  \"almond extract\":7,\n  \"celery seeds\":2,\n  \"allspice\":179,\n  \"dijon mustard\":4,\n  \"soy sauce\":16,\n  \"apples\":47,\n  \"sweet potatoes\":5,\n  \"maple syrup\":11,\n  \"egg whites\":29,\n  \"worcestershire sauce\":17,\n  \"brown sugar\":190,\n  \"lime juice\":9,\n  \"dry mustard\":20,\n  \"white flour\":7,\n  \"mayonnaise\":7,\n  \"chicken wings\":2,\n  \"fresh thyme\":2,\n  \"vegetable oil\":56,\n  \"olive oil\":68,\n  \"flour\":277,\n  \"curry powder\":15,\n  \"vinegar\":105,\n  \"white wine\":11,\n  \"sherry\":7,\n  \"oats\":8,\n  \"yam\":2,\n  \"whipping cream\":10,\n  \"chicken breast\":3,\n  \"sugar\":418},\n \"yellow mustard\":\n {\"sour cream\":2,\n  \"yogurt\":2,\n  \"cream cheese\":2,\n  \"bacon\":2,\n  \"parsley\":2,\n  \"cayenne pepper\":6,\n  \"green olives\":2,\n  \"lemon juice\":2,\n  \"honey\":5,\n  \"beer\":2,\n  \"paprika\":2,\n  \"chili powder\":2,\n  \"red wine vinegar\":8,\n  \"yeast\":2,\n  \"egg yolks\":2,\n  \"green pepper\":2,\n  \"cornstarch\":3,\n  \"dried basil\":2,\n  \"catsup\":3,\n  \"ground beef\":3,\n  \"tomatoes\":2,\n  \"red pepper flakes\":3,\n  \"eggs\":6,\n  \"margarine\":2,\n  \"butter\":3,\n  \"garlic powder\":3,\n  \"dried thyme\":2,\n  \"orange juice\":3,\n  \"black pepper\":10,\n  \"salt\":28,\n  \"lemon\":3,\n  \"potatoes\":4,\n  \"oil\":2,\n  \"allspice\":2,\n  \"dijon mustard\":3,\n  \"soy sauce\":3,\n  \"celery\":4,\n  \"maple syrup\":2,\n  \"nutmeg\":2,\n  \"worcestershire sauce\":9,\n  \"tabasco sauce\":3,\n  \"brown sugar\":9,\n  \"balsamic vinegar\":2,\n  \"mayonnaise\":15,\n  \"green onions\":4,\n  \"olive oil\":5,\n  \"vegetable oil\":2,\n  \"flour\":2,\n  \"kidney beans\":2,\n  \"vinegar\":3,\n  \"white wine\":4,\n  \"carrots\":2,\n  \"sugar\":6},\n \"skim milk\":\n {\"red wine\":2,\n  \"elbow macaroni\":8,\n  \"bacon\":5,\n  \"cayenne pepper\":15,\n  \"lemon juice\":42,\n  \"honey\":33,\n  \"zucchini squash\":2,\n  \"swiss cheese\":6,\n  \"paprika\":27,\n  \"bread\":7,\n  \"ground cinnamon\":69,\n  \"yeast\":10,\n  \"egg yolks\":13,\n  \"green pepper\":13,\n  \"baking powder\":218,\n  \"pine nuts\":4,\n  \"cinnamon\":94,\n  \"cream of tartar\":11,\n  \"cheddar cheese\":7,\n  \"tomatoes\":5,\n  \"white pepper\":17,\n  \"brown rice\":3,\n  \"black beans\":3,\n  \"fresh garlic\":2,\n  \"pinto beans\":2,\n  \"cauliflower\":2,\n  \"butter\":34,\n  \"frozen peas\":7,\n  \"salmon\":3,\n  \"orange juice\":19,\n  \"lemon\":4,\n  \"black pepper\":40,\n  \"buttermilk\":5,\n  \"powdered sugar\":27,\n  \"eggplant\":5,\n  \"dried oregano\":14,\n  \"capers\":2,\n  \"cornmeal\":11,\n  \"cloves\":10,\n  \"oil\":21,\n  \"chicken\":7,\n  \"canola oil\":26,\n  \"dried tarragon\":4,\n  \"strawberries\":5,\n  \"celery\":10,\n  \"alfalfa sprouts\":2,\n  \"fresh peaches\":2,\n  \"fresh cilantro\":2,\n  \"walnuts\":9,\n  \"nutmeg\":43,\n  \"baking soda\":95,\n  \"bread crumbs\":15,\n  \"corn syrup\":9,\n  \"tabasco sauce\":2,\n  \"ginger\":12,\n  \"lime\":2,\n  \"pecans\":5,\n  \"peanut butter\":12,\n  \"cumin\":11,\n  \"green onions\":11,\n  \"frozen corn\":5,\n  \"banana\":11,\n  \"barley\":4,\n  \"kidney beans\":2,\n  \"shortening\":3,\n  \"applesauce\":26,\n  \"parmesan cheese\":22,\n  \"carrots\":18,\n  \"mushrooms\":12,\n  \"sour cream\":4,\n  \"yogurt\":3,\n  \"tofu\":3,\n  \"cream cheese\":5,\n  \"cherry tomatoes\":2,\n  \"salsa\":5,\n  \"parsley\":12,\n  \"molasses\":10,\n  \"chili powder\":17,\n  \"raisins\":52,\n  \"red wine vinegar\":3,\n  \"chives\":2,\n  \"cornstarch\":69,\n  \"dried basil\":11,\n  \"orange\":5,\n  \"ground beef\":3,\n  \"mozzarella cheese\":7,\n  \"sweet red pepper\":2,\n  \"eggs\":90,\n  \"almonds\":4,\n  \"margarine\":139,\n  \"kale\":2,\n  \"garlic powder\":22,\n  \"dried thyme\":5,\n  \"cream of mushroom soup\":5,\n  \"vanilla\":108,\n  \"whole wheat flour\":48,\n  \"salt\":422,\n  \"fresh basil\":2,\n  \"shiitake mushrooms\":2,\n  \"potatoes\":20,\n  \"almond extract\":18,\n  \"frozen spinach\":3,\n  \"allspice\":6,\n  \"dijon mustard\":8,\n  \"soy sauce\":2,\n  \"broccoli\":8,\n  \"apples\":11,\n  \"sweet potatoes\":5,\n  \"maple syrup\":15,\n  \"white rice\":4,\n  \"egg whites\":150,\n  \"worcestershire sauce\":21,\n  \"brown sugar\":56,\n  \"lime juice\":4,\n  \"dry mustard\":31,\n  \"white flour\":5,\n  \"blueberries\":18,\n  \"mayonnaise\":4,\n  \"fresh thyme\":2,\n  \"olive oil\":38,\n  \"vegetable oil\":71,\n  \"flour\":148,\n  \"curry powder\":12,\n  \"vinegar\":5,\n  \"white wine\":6,\n  \"sherry\":4,\n  \"oats\":6,\n  \"whipping cream\":2,\n  \"chicken breast\":2,\n  \"sugar\":282},\n \"cherry tomatoes\":\n {\"sour cream\":2,\n  \"cream cheese\":4,\n  \"bacon\":6,\n  \"cayenne pepper\":3,\n  \"parsley\":11,\n  \"lemon juice\":25,\n  \"rice vinegar\":4,\n  \"honey\":10,\n  \"molasses\":2,\n  \"chili powder\":5,\n  \"paprika\":5,\n  \"raisins\":2,\n  \"red wine vinegar\":17,\n  \"garam masala\":2,\n  \"chives\":2,\n  \"egg yolks\":2,\n  \"green pepper\":20,\n  \"cornstarch\":6,\n  \"saffron\":2,\n  \"dried basil\":10,\n  \"catsup\":3,\n  \"sesame oil\":3,\n  \"radishes\":8,\n  \"ground beef\":2,\n  \"cinnamon\":2,\n  \"pine nuts\":6,\n  \"avocado\":4,\n  \"mozzarella cheese\":4,\n  \"tomatoes\":5,\n  \"cumin seeds\":2,\n  \"white pepper\":4,\n  \"barbecue sauce\":2,\n  \"red pepper flakes\":3,\n  \"black beans\":3,\n  \"eggs\":7,\n  \"almonds\":2,\n  \"fresh garlic\":4,\n  \"margarine\":2,\n  \"cauliflower\":3,\n  \"fresh ginger\":4,\n  \"frozen peas\":3,\n  \"butter\":13,\n  \"garlic powder\":10,\n  \"dried thyme\":6,\n  \"lemon\":4,\n  \"black pepper\":11,\n  \"salt\":64,\n  \"fresh basil\":10,\n  \"black olives\":3,\n  \"potatoes\":5,\n  \"buttermilk\":2,\n  \"eggplant\":5,\n  \"dried oregano\":4,\n  \"capers\":2,\n  \"cloves\":2,\n  \"oil\":9,\n  \"cucumber\":10,\n  \"allspice\":2,\n  \"canola oil\":3,\n  \"dijon mustard\":7,\n  \"soy sauce\":19,\n  \"celery\":5,\n  \"broccoli\":4,\n  \"sweet potatoes\":2,\n  \"fresh cilantro\":2,\n  \"egg whites\":2,\n  \"bread crumbs\":3,\n  \"worcestershire sauce\":6,\n  \"tabasco sauce\":3,\n  \"brown sugar\":5,\n  \"ginger\":5,\n  \"balsamic vinegar\":11,\n  \"lime juice\":5,\n  \"dry mustard\":6,\n  \"fresh rosemary\":2,\n  \"lime\":3,\n  \"pecans\":3,\n  \"peanut butter\":2,\n  \"mayonnaise\":7,\n  \"cumin\":3,\n  \"green onions\":15,\n  \"fresh thyme\":2,\n  \"vegetable oil\":18,\n  \"olive oil\":66,\n  \"skim milk\":2,\n  \"curry powder\":5,\n  \"flour\":4,\n  \"vinegar\":4,\n  \"white wine\":3,\n  \"parmesan cheese\":9,\n  \"carrots\":12,\n  \"yam\":3,\n  \"whipping cream\":2,\n  \"sugar\":21,\n  \"mushrooms\":19},\n \"green pepper\":\n {\"elbow macaroni\":9,\n  \"red wine\":19,\n  \"canned tomato sauce\":2,\n  \"bacon\":57,\n  \"cayenne pepper\":62,\n  \"honey\":22,\n  \"lemon juice\":98,\n  \"swiss cheese\":11,\n  \"zucchini squash\":3,\n  \"paprika\":95,\n  \"bread\":10,\n  \"ground cinnamon\":7,\n  \"egg yolks\":8,\n  \"baking powder\":11,\n  \"saffron\":16,\n  \"sesame oil\":16,\n  \"cream of tartar\":2,\n  \"cinnamon\":24,\n  \"pine nuts\":7,\n  \"avocado\":2,\n  \"cheddar cheese\":74,\n  \"tomatoes\":245,\n  \"white pepper\":22,\n  \"brown rice\":13,\n  \"black beans\":27,\n  \"fresh garlic\":2,\n  \"pinto beans\":19,\n  \"cauliflower\":10,\n  \"frozen peas\":10,\n  \"butter\":145,\n  \"salmon\":5,\n  \"orange juice\":10,\n  \"black pepper\":90,\n  \"lemon\":22,\n  \"tumeric\":7,\n  \"black olives\":27,\n  \"buttermilk\":3,\n  \"hoisin sauce\":5,\n  \"eggplant\":51,\n  \"dried oregano\":40,\n  \"capers\":15,\n  \"cornmeal\":4,\n  \"cloves\":15,\n  \"oil\":165,\n  \"chicken\":47,\n  \"cucumber\":33,\n  \"canola oil\":5,\n  \"flour tortilla\":2,\n  \"dried tarragon\":3,\n  \"celery\":219,\n  \"cornish game hens\":2,\n  \"alfalfa sprouts\":3,\n  \"fresh peaches\":2,\n  \"walnuts\":6,\n  \"nutmeg\":18,\n  \"fresh cilantro\":9,\n  \"bread crumbs\":31,\n  \"baking soda\":6,\n  \"tabasco sauce\":45,\n  \"balsamic vinegar\":2,\n  \"ginger\":21,\n  \"lime\":4,\n  \"pecans\":3,\n  \"peanut butter\":4,\n  \"frozen corn\":9,\n  \"green onions\":84,\n  \"cumin\":109,\n  \"skim milk\":13,\n  \"barley\":5,\n  \"kidney beans\":43,\n  \"shortening\":10,\n  \"parmesan cheese\":43,\n  \"carrots\":114,\n  \"mushrooms\":122,\n  \"sour cream\":62,\n  \"yogurt\":3,\n  \"tofu\":6,\n  \"cream cheese\":19,\n  \"cherry tomatoes\":20,\n  \"salsa\":19,\n  \"parsley\":104,\n  \"green olives\":10,\n  \"rice vinegar\":3,\n  \"molasses\":11,\n  \"chili powder\":204,\n  \"beer\":5,\n  \"spaghetti\":13,\n  \"raisins\":18,\n  \"red wine vinegar\":34,\n  \"garam masala\":4,\n  \"chives\":5,\n  \"cornstarch\":115,\n  \"dried basil\":37,\n  \"orange\":5,\n  \"radishes\":12,\n  \"catsup\":22,\n  \"ground beef\":117,\n  \"romaine lettuce\":2,\n  \"mozzarella cheese\":31,\n  \"sweet red pepper\":24,\n  \"cumin seeds\":4,\n  \"barbecue sauce\":6,\n  \"raw spinach\":2,\n  \"red pepper flakes\":13,\n  \"eggs\":97,\n  \"almonds\":9,\n  \"margarine\":40,\n  \"fresh ginger\":6,\n  \"garlic powder\":91,\n  \"dried thyme\":15,\n  \"cream of mushroom soup\":23,\n  \"whole wheat flour\":3,\n  \"salt\":767,\n  \"fresh basil\":8,\n  \"bean sprouts\":9,\n  \"potatoes\":56,\n  \"celery seeds\":3,\n  \"allspice\":15,\n  \"dijon mustard\":17,\n  \"soy sauce\":132,\n  \"broccoli\":10,\n  \"chicken legs\":2,\n  \"apples\":5,\n  \"shell macaroni\":2,\n  \"maple syrup\":3,\n  \"sweet potatoes\":6,\n  \"yellow mustard\":2,\n  \"white rice\":7,\n  \"egg whites\":5,\n  \"worcestershire sauce\":103,\n  \"dried cilantro\":2,\n  \"brown sugar\":72,\n  \"lime juice\":12,\n  \"dry mustard\":52,\n  \"raw shrimp\":5,\n  \"mayonnaise\":62,\n  \"chicken wings\":2,\n  \"fresh thyme\":2,\n  \"vegetable oil\":140,\n  \"olive oil\":223,\n  \"curry powder\":43,\n  \"flour\":140,\n  \"vinegar\":123,\n  \"white wine\":21,\n  \"sherry\":16,\n  \"whipping cream\":4,\n  \"chicken breast\":12,\n  \"sugar\":202},\n \"adzuki beans\":\n {\"lime juice\":2,\n  \"potatoes\":2,\n  \"parsley\":3,\n  \"green onions\":2,\n  \"oil\":2,\n  \"honey\":2,\n  \"vegetable oil\":4,\n  \"brown rice\":2,\n  \"black beans\":2,\n  \"soy sauce\":3,\n  \"butter\":2,\n  \"garlic powder\":2,\n  \"white wine\":2,\n  \"parmesan cheese\":2,\n  \"lemon\":2,\n  \"salt\":3,\n  \"sugar\":2},\n \"kidney beans\":\n {\"sour cream\":10,\n  \"yogurt\":2,\n  \"elbow macaroni\":2,\n  \"red wine\":4,\n  \"tofu\":3,\n  \"bacon\":18,\n  \"salsa\":5,\n  \"cayenne pepper\":15,\n  \"parsley\":12,\n  \"honey\":3,\n  \"lemon juice\":8,\n  \"swiss cheese\":2,\n  \"molasses\":2,\n  \"beer\":3,\n  \"chili powder\":104,\n  \"paprika\":14,\n  \"ground cinnamon\":2,\n  \"raisins\":4,\n  \"red wine vinegar\":4,\n  \"chives\":2,\n  \"garam masala\":3,\n  \"egg yolks\":2,\n  \"green pepper\":43,\n  \"baking powder\":2,\n  \"cornstarch\":6,\n  \"dried basil\":8,\n  \"radishes\":2,\n  \"catsup\":5,\n  \"cinnamon\":3,\n  \"ground beef\":45,\n  \"avocado\":7,\n  \"cheddar cheese\":17,\n  \"sweet red pepper\":2,\n  \"tomatoes\":59,\n  \"cumin seeds\":3,\n  \"white pepper\":2,\n  \"barbecue sauce\":4,\n  \"brown rice\":7,\n  \"red pepper flakes\":6,\n  \"black beans\":12,\n  \"margarine\":3,\n  \"pinto beans\":20,\n  \"cauliflower\":2,\n  \"fresh ginger\":2,\n  \"frozen peas\":2,\n  \"butter\":9,\n  \"garlic powder\":18,\n  \"dried thyme\":3,\n  \"cream of mushroom soup\":2,\n  \"lemon\":2,\n  \"salt\":104,\n  \"black pepper\":21,\n  \"tumeric\":2,\n  \"black olives\":3,\n  \"potatoes\":3,\n  \"dried oregano\":9,\n  \"cornmeal\":3,\n  \"cloves\":4,\n  \"oil\":22,\n  \"chicken\":3,\n  \"canola oil\":2,\n  \"celery seeds\":2,\n  \"allspice\":3,\n  \"soy sauce\":5,\n  \"celery\":25,\n  \"apples\":2,\n  \"yellow mustard\":2,\n  \"alfalfa sprouts\":2,\n  \"fresh cilantro\":3,\n  \"nutmeg\":2,\n  \"egg whites\":2,\n  \"bread crumbs\":2,\n  \"worcestershire sauce\":9,\n  \"tabasco sauce\":6,\n  \"brown sugar\":20,\n  \"balsamic vinegar\":2,\n  \"ginger\":2,\n  \"dry mustard\":6,\n  \"mayonnaise\":2,\n  \"cumin\":41,\n  \"green onions\":6,\n  \"vegetable oil\":16,\n  \"olive oil\":35,\n  \"skim milk\":2,\n  \"barley\":5,\n  \"flour\":7,\n  \"curry powder\":4,\n  \"vinegar\":23,\n  \"parmesan cheese\":4,\n  \"carrots\":21,\n  \"sugar\":25,\n  \"mushrooms\":7},\n \"hoisin sauce\":\n {\"parsley\":2,\n  \"cayenne pepper\":7,\n  \"honey\":38,\n  \"rice vinegar\":5,\n  \"lemon juice\":7,\n  \"molasses\":3,\n  \"beer\":2,\n  \"yeast\":2,\n  \"red wine vinegar\":2,\n  \"egg yolks\":3,\n  \"green pepper\":5,\n  \"cornstarch\":42,\n  \"catsup\":13,\n  \"sesame oil\":48,\n  \"pine nuts\":5,\n  \"ground beef\":3,\n  \"mozzarella cheese\":2,\n  \"sweet red pepper\":2,\n  \"tomatoes\":2,\n  \"white pepper\":4,\n  \"red pepper flakes\":3,\n  \"black beans\":3,\n  \"eggs\":7,\n  \"fresh garlic\":2,\n  \"fresh ginger\":10,\n  \"butter\":3,\n  \"garlic powder\":2,\n  \"orange juice\":7,\n  \"lemon\":4,\n  \"black pepper\":6,\n  \"salt\":51,\n  \"fresh basil\":2,\n  \"bean sprouts\":8,\n  \"shiitake mushrooms\":2,\n  \"eggplant\":2,\n  \"oil\":23,\n  \"chicken\":4,\n  \"cucumber\":2,\n  \"canola oil\":3,\n  \"dijon mustard\":4,\n  \"soy sauce\":100,\n  \"broccoli\":2,\n  \"maple syrup\":3,\n  \"cornish game hens\":4,\n  \"walnuts\":2,\n  \"fresh cilantro\":4,\n  \"worcestershire sauce\":4,\n  \"tabasco sauce\":2,\n  \"brown sugar\":4,\n  \"ginger\":14,\n  \"balsamic vinegar\":4,\n  \"lime\":3,\n  \"pecans\":2,\n  \"peanut butter\":4,\n  \"mayonnaise\":2,\n  \"green onions\":26,\n  \"cumin\":2,\n  \"chicken wings\":3,\n  \"vegetable oil\":14,\n  \"olive oil\":8,\n  \"curry powder\":2,\n  \"flour\":3,\n  \"vinegar\":4,\n  \"white wine\":3,\n  \"carrots\":8,\n  \"sherry\":26,\n  \"chicken breast\":2,\n  \"mushrooms\":4,\n  \"sugar\":69},\n \"dry rosemary\":\n {\"cornstarch\":2,\n  \"cream cheese\":2,\n  \"cayenne pepper\":2,\n  \"green onions\":2,\n  \"honey\":2,\n  \"oil\":2,\n  \"lemon juice\":2,\n  \"olive oil\":4,\n  \"white pepper\":2,\n  \"almonds\":2,\n  \"dijon mustard\":3,\n  \"raisins\":2,\n  \"carrots\":2,\n  \"nutmeg\":2,\n  \"sugar\":2,\n  \"salt\":2,\n  \"black pepper\":2},\n \"cornstarch\":\n {\"elbow macaroni\":4,\n  \"canned beef broth\":4,\n  \"red wine\":10,\n  \"bacon\":33,\n  \"cayenne pepper\":33,\n  \"lemon juice\":341,\n  \"honey\":124,\n  \"swiss cheese\":4,\n  \"paprika\":67,\n  \"ground cinnamon\":65,\n  \"bread\":7,\n  \"yeast\":4,\n  \"egg yolks\":124,\n  \"green pepper\":115,\n  \"baking powder\":132,\n  \"saffron\":7,\n  \"sesame oil\":266,\n  \"pine nuts\":11,\n  \"cinnamon\":151,\n  \"cream of tartar\":58,\n  \"avocado\":3,\n  \"cheddar cheese\":10,\n  \"tomatoes\":64,\n  \"white pepper\":95,\n  \"brown rice\":5,\n  \"black beans\":4,\n  \"pinto beans\":2,\n  \"cauliflower\":11,\n  \"frozen peas\":15,\n  \"butter\":407,\n  \"salmon\":2,\n  \"orange juice\":140,\n  \"lemon\":39,\n  \"black pepper\":86,\n  \"tumeric\":4,\n  \"black olives\":2,\n  \"buttermilk\":16,\n  \"powdered sugar\":47,\n  \"dried oregano\":17,\n  \"eggplant\":8,\n  \"hoisin sauce\":42,\n  \"capers\":11,\n  \"cornmeal\":8,\n  \"cloves\":26,\n  \"oil\":731,\n  \"chicken\":62,\n  \"blue cheese\":2,\n  \"cucumber\":11,\n  \"dry rosemary\":2,\n  \"canola oil\":12,\n  \"dried tarragon\":5,\n  \"strawberries\":39,\n  \"celery\":98,\n  \"cornish game hens\":4,\n  \"fresh cilantro\":5,\n  \"fresh peaches\":7,\n  \"nutmeg\":84,\n  \"walnuts\":21}\n}\n  "
  },
  {
    "path": "cooking_recipes/data/desert.json",
    "content": "[\n    {\n        \"name\": \"Easy Cake\",\n        \"num_served\": 8,\n        \"ingredients\": [\n            {\n                \"name\": \"butter\",\n                \"amount\": \"1\",\n                \"units\": \"cup\",\n                \"description\": \"Butter\"\n            },\n            {\n                \"name\": \"brown sugar\",\n                \"amount\": \"2\",\n                \"units\": \"cup\",\n                \"description\": \"Sugar\"\n            },\n            {\n                \"name\": \"flour\",\n                \"amount\": \"3\",\n                \"units\": \"cup\",\n                \"description\": \"Flour\"\n            },\n            {\n                \"name\": \"eggs\",\n                \"amount\": \"4\",\n                \"units\": \"\",\n                \"description\": \"Eggs\"\n            },\n            {\n                \"name\": \"baking powder\",\n                \"amount\": \"5\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Baking powder\"\n            },\n            {\n                \"name\": \"skim milk\",\n                \"amount\": \"1\",\n                \"units\": \"cup\",\n                \"description\": \"Milk\"\n            },\n            {\n                \"name\": \"vanilla (extract)\",\n                \"amount\": \"1\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Vanilla extract\"\n            }\n        ],\n        \"directions\": [\"Preheat oven to 350F.\",\n            \"Mix together butter, sugar, eggs and vanilla extract in a mixing bowl.\",\n            \"Mix in milk and then baking powder. Mix in flour, stirring lightly.\",\n            \"Bake for 30  minutes.\"]\n    },\n    {\n        \"name\": \"Apple Muffins\",\n        \"num_served\": 10,\n        \"ingredients\": [\n            {\n                \"name\": \"egg\",\n                \"amount\": \"1\",\n                \"units\": \"\",\n                \"description\": \"Egg\"\n            },\n            {\n                \"name\": \"milk\",\n                \"amount\": \"1/2\",\n                \"units\": \"cup\",\n                \"description\": \"Milk\"\n            },\n            {\n                \"name\": \"vegetable oil\",\n                \"amount\": \"1/4\",\n                \"units\": \"cup\",\n                \"description\": \"Vegetable oil\"\n            },\n            {\n                \"name\": \"applesauce\",\n                \"amount\": \"1/2\",\n                \"units\": \"cup\",\n                \"description\": \"Applesauce\"\n            },\n            {\n                \"name\": \"flour\",\n                \"amount\": \"1 1/2\",\n                \"units\": \"cup\",\n                \"description\": \"Flour\"\n            },\n            {\n                \"name\": \"brown sugar\",\n                \"amount\": \"3/4\",\n                \"units\": \"cup\",\n                \"description\": \"Sugar\"\n            },\n            {\n                \"name\": \"baking powder\",\n                \"amount\": \"2\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Baking powder\"\n            },\n            {\n                \"name\": \"cinnamon\",\n                \"amount\": \"1/2\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Cinnamon\"\n            },\n            {\n                \"name\": \"salt\",\n                \"amount\": \"1/2\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Salt\"\n            }\n        ],\n        \"directions\": [\"preheat oven to 375F.\",\n            \"Beat egg in a bowl and then stir in milk, oil, and the applesauce.\",\n            \"Then mix in the flour, most of the brown sugar, baking powder, cinnamon, and salt until everything is moist. Place batter in paper baking cups - about half to two thirds full. Sprinkle remainder of brown sugar top of muffins.\",\n            \"Bake 25 to 30 minutes.\"]\n    },\n    {\n        \"name\": \"Irene's Snickerdoodle Cookies\",\n        \"num_served\": 10,\n        \"ingredients\": [\n            {\n                \"name\": \"shortening (vegetable)\",\n                \"amount\": \"1\",\n                \"units\": \"cup\",\n                \"description\": \"Shortening\"\n            },\n            {\n                \"name\": \"sugar\",\n                \"amount\": \"1 3/4\",\n                \"units\": \"cup\",\n                \"description\": \"Sugar\"\n            },\n            {\n                \"name\": \"eggs\",\n                \"amount\": \"2\",\n                \"units\": \"\",\n                \"description\": \"Eggs\"\n            },\n            {\n                \"name\": \"flour\",\n                \"amount\": \"3\",\n                \"units\": \"cup\",\n                \"description\": \"Flour\"\n            },\n            {\n                \"name\": \"cream of tartar\",\n                \"amount\": \"2\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Cream of tartar\"\n            },\n            {\n                \"name\": \"baking soda\",\n                \"amount\": \"1\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Soda\"\n            },\n            {\n                \"name\": \"salt\",\n                \"amount\": \"1/4\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Salt\"\n            },\n            {\n                \"name\": \"cinnamon\",\n                \"amount\": \"1\",\n                \"units\": \"dash\",\n                \"description\": \"Cinnamon sugar\"\n            }\n        ],\n        \"directions\": [\"preheat oven to 375F.\",\n            \"Mix together the vegetable shortening, sugar and eggs in a small mixing bowl.\",\n            \"Sift together flour and mix well with the cream of tartar, soda and salt.\",\n            \"Mix together both mixtures and roll into  balls about one inch in diameter and roll in cinnamon and  sugar.\",\n            \"Put on a lightly greased baking sheet, leaving about 1 1/2 inches between the cookies.\",\n            \"Bake from 10 to 12 minutes until done.\"]\n    },\n    {\n        \"name\": \"Scottish Shortbread\",\n        \"num_served\":10,\n        \"ingredients\": [\n            {\n                \"name\": \"flour\",\n                \"amount\": \"4\",\n                \"units\": \"cup\",\n                \"description\": \"Plain flour\"\n            },\n            {\n                \"name\": \"butter\",\n                \"amount\": \"2\",\n                \"units\": \"cup\",\n                \"description\": \"Butter (nothing else will do)\"\n            },\n            {\n                \"name\": \"sugar\",\n                \"amount\": \"1/2\",\n                \"units\": \"cup\",\n                \"description\": \"Sugar\"\n            },\n            {\n                \"name\": \"salt\",\n                \"amount\": \"1\",\n                \"units\": \"dash\",\n                \"description\": \"Salt\"\n            }\n        ],\n        \"directions\": [\"Preheat oven to 350F\",\n            \"Sift flour on a large board. Place the sugar in a separate area on the board and mix in the butter.\",\n            \"Knead in flour adding small amounts at a time.\",\n            \"Grease a baking sheet. Roll dough into small balls, about one inch in diameter.\",\n            \"Flatten the balls and place on the baking sheet.\",\n            \"Bake for 30 minutes.\"]\n    }\n\n\n]"
  },
  {
    "path": "cooking_recipes/data/fish.json",
    "content": "[\n    {\"name\":\"Honey Chinese Shrimp\",\n        \"num_served\":4,\n        \"ingredients\":\n            [{\"name\":\"honey\",\n                \"amount\":\"1/4\",\n                \"units\":\"cup\",\n                \"description\":\"Honey\"},\n                {\"name\":\"raw shrimp (medium-large)\",\n                    \"amount\":\"16\",\n                    \"units\":\"\",\n                    \"description\":\"\"},\n                {\"name\":\"red wine\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"cup\",\n                    \"description\":\"Red wine\"},\n                {\"name\":\"sesame oil\",\n                    \"amount\":\"2\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Sesame oil\"},\n                {\"name\":\"soy sauce\",\n                    \"amount\":\"1.5\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Soy Sauce\"},\n                {\"name\":\"worcestershire sauce (optional)\",\n                    \"amount\":\"1\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Worcestershire\"},\n                {\"name\":\"Sriracha Hot Chili Sauce (optional, or substitute any chili sauce)\",\n                    \"amount\":\"2\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Sriracha Hot Chili Sauce\"},\n                {\"name\":\"orange juice\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"cup\",\n                    \"description\":\"Orange juice\"},\n                {\"name\":\"black pepper\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Black pepper\"}],\n        \"directions\":\n            [\"Mix together all ingredients and marinate in the refrigerator for at least one hour.\",\n                \"There are two ways to cook this dish:\",\n                \"1. Barbecue on a grill, occasionally basting with more marinade.\",\n                \"2. Remove shrimp from marinade sauce and fry in a hot non-stick frying pan.\",\n                \"This is good served over brown or white rice with a side of steamed vegetables.\"]},\n\n    {\n        \"name\": \"Chinese Shrimp with Zucchini Squash\",\n        \"num_served\": 2,\n        \"ingredients\": [\n            {\n                \"name\": \"raw shrimp (or defrosted frozen)\",\n                \"amount\": \"14\",\n                \"units\": \"medium-large\",\n                \"description\": \"medium shrimp\"\n            },\n            {\n                \"name\": \"cornstarch\",\n                \"amount\": \"1\",\n                \"units\": \"tablespoon\",\n                \"description\": \"Cornstarch\"\n            },\n            {\n                \"name\": \"salt\",\n                \"amount\": \"1/2\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Salt\"\n            },\n            {\n                \"name\": \"black pepper\",\n                \"amount\": \"1/2\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Pepper\"\n            },\n            {\n                \"name\": \"zucchini squash (chopped)\",\n                \"amount\": \"2\",\n                \"units\": \"cup\",\n                \"description\": \"Zucchini\"\n            },\n            {\n                \"name\": \"vegetable oil\",\n                \"amount\": \"2\",\n                \"units\": \"tablespoon\",\n                \"description\": \"Oil\"\n            },\n            {\n                \"name\": \"sherry\",\n                \"amount\": \"1\",\n                \"units\": \"tablespoon\",\n                \"description\": \"Sherry\"\n            },\n            {\n                \"name\": \"soy sauce\",\n                \"amount\": \"1\",\n                \"units\": \"tablespoon\",\n                \"description\": \"Soy sauce\"\n            }\n        ],\n        \"directions\": [\"I vary the proportion of shrimp to Zucchini squash depending on both my tastes and how much shrimp and Zucchini I have on-hand that I want to use up.\",\n            \"Shell and chop the shrimps into three or four pieces and roll in cornstarch, salt and  pepper - use a small bowl for this.\",\n            \"Wash the zucchini and coarsely chop.\",\n            \"Heat the oil in a non-stick pan and fry Zucchini until it is mostly done before adding the shrimp and the sherry.\",\n            \"When the shrimp are done then mix in the soy sauce and serve immediately.\",\n            \"Note: I like to eat this dish as-is but you might want to also try serving it with cooked Chinese style noodles or even a small amount of cooked Italian pasta.\"]\n    }\n\n]"
  },
  {
    "path": "cooking_recipes/data/ingredients.json",
    "content": "{\"101010101\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.0,\n  \"phosphorus\":0.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.0,\n  \"selenium\":0.0,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.0,\n  \"sugar\":-0.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":0.0,\n  \"fat_poly_unsaturated\":0.0,\n  \"lipid_total\":0.0,\n  \"prompt\":\n  \"NULL NO NUTRIENT INGREDIENT -- 1 cup\",\n  \"vitamin_e\":0.0,\n  \"vitamin_b6\":0.0,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":0.0,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.0,\n  \"fat_saturated\":0.0,\n  \"weight1_gram\":0.0,\n  \"folate_dfe\":0.0,\n  \"choline\":0.0,\n  \"sodium\":0.0,\n  \"magnesium\":0.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":0.0,\n  \"ndb_no\":1083011,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":0.0,\n  \"id\":101010101,\n  \"energy_in_kcal\":0.0,\n  \"fat_mono_unsaturated\":0.0,\n  \"description\":\n  \"NULL NO NUTRIENT INGREDIENT -- 1 cup\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":0.0,\n  \"zinc\":0.0,\n  \"riboflavin\":0.0,\n  \"copper\":0.0},\n \"10101022\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":6.0,\n  \"pantothenic_acid\":0.0,\n  \"phosphorus\":0.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.0,\n  \"selenium\":0.0,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.0,\n  \"sugar\":-0.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":0.0,\n  \"fat_poly_unsaturated\":0.0,\n  \"lipid_total\":0.0,\n  \"prompt\":\n  \"RED PEPPER FLAKES -- 1 tablespoon\",\n  \"vitamin_e\":0.0,\n  \"vitamin_b6\":0.0,\n  \"fiber\":0.0,\n  \"vitamin_c\":20.0,\n  \"protein\":0.0,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.0,\n  \"fat_saturated\":0.0,\n  \"weight1_gram\":9.0,\n  \"folate_dfe\":0.0,\n  \"choline\":0.0,\n  \"sodium\":0.0,\n  \"magnesium\":0.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 tablespoon\",\n  \"calcium\":0.0,\n  \"ndb_no\":1083011,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":0.0,\n  \"id\":10101022,\n  \"energy_in_kcal\":0.0,\n  \"fat_mono_unsaturated\":0.0,\n  \"description\":\n  \"RED PEPPER FLAKES -- 1 tablespoon\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":0.0,\n  \"zinc\":0.0,\n  \"riboflavin\":0.0,\n  \"copper\":0.0},\n \"10101674\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":5.0,\n  \"pantothenic_acid\":0.0,\n  \"phosphorus\":4.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.0,\n  \"selenium\":0.0,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":2.0,\n  \"sugar\":-0.0,\n  \"vitamin_b12\":3.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":0.0,\n  \"fat_poly_unsaturated\":2.0,\n  \"lipid_total\":0.0,\n  \"prompt\":\n  \"CHICKEN BROTH -- 1 cup\",\n  \"vitamin_e\":0.0,\n  \"vitamin_b6\":0.0,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":6.0,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.0,\n  \"fat_saturated\":0.0,\n  \"weight1_gram\":236.0,\n  \"folate_dfe\":0.0,\n  \"choline\":0.0,\n  \"sodium\":30.0,\n  \"magnesium\":1.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":0.0,\n  \"ndb_no\":1083011,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":3.0,\n  \"id\":101010101,\n  \"energy_in_kcal\":0.0,\n  \"fat_mono_unsaturated\":0.0,\n  \"description\":\n  \"CHICKEN BROTH -- 1 cup\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":0.0,\n  \"zinc\":1.0,\n  \"riboflavin\":3.0,\n  \"copper\":0.0},\n \"512\":\n {\"beta_carotene\":-1.0,\n  \"lycopene\":-1.0,\n  \"vitamin_a_rae\":59.0,\n  \"pantothenic_acid\":0.336,\n  \"phosphorus\":100.0,\n  \"beta_crytoxanthin\":-1.0,\n  \"thiamin\":0.04,\n  \"selenium\":2.3,\n  \"vitamin_k\":-1.0,\n  \"alpha_carotene\":-1.0,\n  \"carbohydrate\":4.97,\n  \"sugar\":-1.0,\n  \"vitamin_b12\":0.38,\n  \"cholesterol\":4.0,\n  \"retinol\":58.0,\n  \"potassium\":162.0,\n  \"fat_poly_unsaturated\":0.036,\n  \"lipid_total\":0.97,\n  \"prompt\":\n  \"MILK,LOWFAT,FLUID,1% MILKFAT,W\\/ NONFAT MILK SOL&VIT A -- 1 cup\",\n  \"vitamin_e\":-1.0,\n  \"vitamin_b6\":0.045,\n  \"fiber\":0.0,\n  \"vitamin_c\":1.0,\n  \"protein\":3.48,\n  \"vitamin_a\":204.0,\n  \"niacin\":0.09,\n  \"fat_saturated\":0.604,\n  \"weight1_gram\":245.0,\n  \"folate_dfe\":5.0,\n  \"choline\":-1.0,\n  \"sodium\":52.0,\n  \"magnesium\":14.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":128.0,\n  \"ndb_no\":1083,\n  \"folate_total\":5.0,\n  \"lutein_zeaxanthin\":-1.0,\n  \"iron\":0.05,\n  \"id\":512,\n  \"energy_in_kcal\":43.0,\n  \"fat_mono_unsaturated\":0.28,\n  \"description\":\n  \"MILK,LOWFAT,FLUID,1% MILKFAT,W\\/ NONFAT MILK SOL&VIT A\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":5.0,\n  \"manganese\":0.002,\n  \"zinc\":0.4,\n  \"riboflavin\":0.173,\n  \"copper\":0.01},\n \"3008\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":1.0,\n  \"pantothenic_acid\":0.313,\n  \"phosphorus\":575.0,\n  \"beta_crytoxanthin\":9.0,\n  \"thiamin\":0.364,\n  \"selenium\":0.7,\n  \"vitamin_k\":53.9,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":13.08,\n  \"sugar\":3.59,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":597.0,\n  \"fat_poly_unsaturated\":34.071,\n  \"lipid_total\":68.37,\n  \"prompt\":\"NUTS,PINE NUTS,DRIED -- 1 cup\",\n  \"vitamin_e\":9.33,\n  \"vitamin_b6\":0.094,\n  \"fiber\":3.7,\n  \"vitamin_c\":0.8,\n  \"protein\":13.69,\n  \"vitamin_a\":29.0,\n  \"niacin\":4.387,\n  \"fat_saturated\":4.899,\n  \"weight1_gram\":135.08,\n  \"folate_dfe\":34.0,\n  \"choline\":55.8,\n  \"sodium\":2.0,\n  \"magnesium\":251.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":16.0,\n  \"ndb_no\":12147,\n  \"folate_total\":34.0,\n  \"lutein_zeaxanthin\":53.9,\n  \"iron\":5.53,\n  \"id\":3008,\n  \"energy_in_kcal\":673.0,\n  \"fat_mono_unsaturated\":18.764,\n  \"description\":\"NUTS,PINE NUTS,DRIED\",\n  \"refuse_percent\":23.0,\n  \"food_folate\":34.0,\n  \"manganese\":8.802,\n  \"zinc\":6.45,\n  \"riboflavin\":0.227,\n  \"copper\":1.324},\n \"2529\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":14.0,\n  \"pantothenic_acid\":0.132,\n  \"phosphorus\":11.0,\n  \"beta_crytoxanthin\":77.0,\n  \"thiamin\":0.013,\n  \"selenium\":0.4,\n  \"vitamin_k\":2.2,\n  \"alpha_carotene\":58.0,\n  \"carbohydrate\":23.98,\n  \"sugar\":22.18,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":130.0,\n  \"fat_poly_unsaturated\":0.064,\n  \"lipid_total\":0.13,\n  \"prompt\":\"PEACHES,FRZ,SLICED,SWTND -- 1 cup, thawed\",\n  \"vitamin_e\":0.62,\n  \"vitamin_b6\":0.018,\n  \"fiber\":1.8,\n  \"vitamin_c\":94.2,\n  \"protein\":0.63,\n  \"vitamin_a\":284.0,\n  \"niacin\":0.653,\n  \"fat_saturated\":0.014,\n  \"weight1_gram\":250.0,\n  \"folate_dfe\":3.0,\n  \"choline\":5.1,\n  \"sodium\":6.0,\n  \"magnesium\":5.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, thawed\",\n  \"calcium\":3.0,\n  \"ndb_no\":9250,\n  \"folate_total\":3.0,\n  \"lutein_zeaxanthin\":2.2,\n  \"iron\":0.37,\n  \"id\":2529,\n  \"energy_in_kcal\":94.0,\n  \"fat_mono_unsaturated\":0.048,\n  \"description\":\"PEACHES,FRZ,SLICED,SWTND\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":3.0,\n  \"manganese\":0.029,\n  \"zinc\":0.05,\n  \"riboflavin\":0.035,\n  \"copper\":0.024},\n \"3649\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":50.0,\n  \"pantothenic_acid\":0.075,\n  \"phosphorus\":37.0,\n  \"beta_crytoxanthin\":1137.0,\n  \"thiamin\":0.055,\n  \"selenium\":0.6,\n  \"vitamin_k\":207.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":7.34,\n  \"sugar\":2.33,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":276.0,\n  \"fat_poly_unsaturated\":0.074,\n  \"lipid_total\":0.19,\n  \"prompt\":\n  \"ONIONS,SPRING OR SCALLIONS (INCL TOPS&BULB),RAW -- 1 cup, chopped\",\n  \"vitamin_e\":0.55,\n  \"vitamin_b6\":0.061,\n  \"fiber\":2.6,\n  \"vitamin_c\":18.8,\n  \"protein\":1.83,\n  \"vitamin_a\":997.0,\n  \"niacin\":0.525,\n  \"fat_saturated\":0.032,\n  \"weight1_gram\":100.0,\n  \"folate_dfe\":64.0,\n  \"choline\":5.7,\n  \"sodium\":16.0,\n  \"magnesium\":20.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, chopped\",\n  \"calcium\":72.0,\n  \"ndb_no\":11291,\n  \"folate_total\":64.0,\n  \"lutein_zeaxanthin\":207.0,\n  \"iron\":1.48,\n  \"id\":3649,\n  \"energy_in_kcal\":32.0,\n  \"fat_mono_unsaturated\":0.027,\n  \"description\":\"ONIONS,SPRING OR SCALLIONS (INCL TOPS&BULB),RAW\",\n  \"refuse_percent\":4.0,\n  \"food_folate\":64.0,\n  \"manganese\":0.16,\n  \"zinc\":0.39,\n  \"riboflavin\":0.08,\n  \"copper\":0.083},\n \"4801\":\n {\"beta_carotene\":-1.0,\n  \"lycopene\":-1.0,\n  \"vitamin_a_rae\":-1.0,\n  \"pantothenic_acid\":-1.0,\n  \"phosphorus\":300.0,\n  \"beta_crytoxanthin\":-1.0,\n  \"thiamin\":0.253,\n  \"selenium\":-1.0,\n  \"vitamin_k\":-1.0,\n  \"alpha_carotene\":-1.0,\n  \"carbohydrate\":64.2,\n  \"sugar\":8.04,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":-1.0,\n  \"potassium\":93.0,\n  \"fat_poly_unsaturated\":1.77,\n  \"lipid_total\":22.9,\n  \"prompt\":\"NABISCO,NABISCO RITZ CRACKERS -- 1 serving\",\n  \"vitamin_e\":-1.0,\n  \"vitamin_b6\":0.036,\n  \"fiber\":1.9,\n  \"vitamin_c\":0.0,\n  \"protein\":7.2,\n  \"vitamin_a\":0.0,\n  \"niacin\":3.81,\n  \"fat_saturated\":3.92,\n  \"weight1_gram\":16.0,\n  \"folate_dfe\":-1.0,\n  \"choline\":-1.0,\n  \"sodium\":776.0,\n  \"magnesium\":20.0,\n  \"folic_acid\":-1.0,\n  \"weight1_description\":\"1 serving\",\n  \"calcium\":147.0,\n  \"ndb_no\":18621,\n  \"folate_total\":60.0,\n  \"lutein_zeaxanthin\":-1.0,\n  \"iron\":4.05,\n  \"id\":4801,\n  \"energy_in_kcal\":492.0,\n  \"fat_mono_unsaturated\":17.95,\n  \"description\":\"NABISCO,NABISCO RITZ CRACKERS\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":-1.0,\n  \"manganese\":-1.0,\n  \"zinc\":1.45,\n  \"riboflavin\":0.306,\n  \"copper\":-1.0},\n \"6913\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.285,\n  \"phosphorus\":83.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.096,\n  \"selenium\":9.8,\n  \"vitamin_k\":0.6,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":22.96,\n  \"sugar\":0.35,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":43.0,\n  \"fat_poly_unsaturated\":0.323,\n  \"lipid_total\":0.9,\n  \"prompt\":\"RICE,BROWN,LONG-GRAIN,CKD -- 1 cup\",\n  \"vitamin_e\":0.03,\n  \"vitamin_b6\":0.145,\n  \"fiber\":1.8,\n  \"vitamin_c\":0.0,\n  \"protein\":2.58,\n  \"vitamin_a\":0.0,\n  \"niacin\":1.528,\n  \"fat_saturated\":0.18,\n  \"weight1_gram\":195.0,\n  \"folate_dfe\":4.0,\n  \"choline\":9.2,\n  \"sodium\":5.0,\n  \"magnesium\":43.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":10.0,\n  \"ndb_no\":20037,\n  \"folate_total\":4.0,\n  \"lutein_zeaxanthin\":0.6,\n  \"iron\":0.42,\n  \"id\":6913,\n  \"energy_in_kcal\":111.0,\n  \"fat_mono_unsaturated\":0.327,\n  \"description\":\"RICE,BROWN,LONG-GRAIN,CKD\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":4.0,\n  \"manganese\":0.905,\n  \"zinc\":0.63,\n  \"riboflavin\":0.025,\n  \"copper\":0.1},\n \"929\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.0,\n  \"phosphorus\":0.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.0,\n  \"selenium\":0.0,\n  \"vitamin_k\":13.6,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.0,\n  \"sugar\":0.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":0.0,\n  \"fat_poly_unsaturated\":41.7,\n  \"lipid_total\":100.0,\n  \"prompt\":\"OIL,SESAME,SALAD OR COOKING -- 1 tablespoon\",\n  \"vitamin_e\":1.4,\n  \"vitamin_b6\":0.0,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":0.0,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.0,\n  \"fat_saturated\":14.2,\n  \"weight1_gram\":13.6,\n  \"folate_dfe\":0.0,\n  \"choline\":0.2,\n  \"sodium\":0.0,\n  \"magnesium\":0.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 tablespoon\",\n  \"calcium\":0.0,\n  \"ndb_no\":4058,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":13.6,\n  \"iron\":0.0,\n  \"id\":929,\n  \"energy_in_kcal\":884.0,\n  \"fat_mono_unsaturated\":39.7,\n  \"description\":\"OIL,SESAME,SALAD OR COOKING\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":-1.0,\n  \"zinc\":0.0,\n  \"riboflavin\":0.0,\n  \"copper\":0.0},\n \"5057\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.63,\n  \"phosphorus\":166.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.05,\n  \"selenium\":30.6,\n  \"vitamin_k\":4.3,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.0,\n  \"sugar\":0.0,\n  \"vitamin_b12\":2.28,\n  \"cholesterol\":106.0,\n  \"retinol\":0.0,\n  \"potassium\":257.0,\n  \"fat_poly_unsaturated\":0.97,\n  \"lipid_total\":13.46,\n  \"prompt\":\n  \"LAMB,DOM,FORESHANK,LN&FAT,1\\/4\\\"FAT,CHOIC,CKD,BRSD -- 1 piece, cooked, excluding refuse (yield from 1 lb raw meat with refuse)\",\n  \"vitamin_e\":0.18,\n  \"vitamin_b6\":0.1,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":28.37,\n  \"vitamin_a\":0.0,\n  \"niacin\":5.46,\n  \"fat_saturated\":5.63,\n  \"weight1_gram\":148.0,\n  \"folate_dfe\":17.0,\n  \"choline\":110.0,\n  \"sodium\":72.0,\n  \"magnesium\":22.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\n  \"1 piece, cooked, excluding refuse (yield from 1 lb raw meat with refuse)\",\n  \"calcium\":20.0,\n  \"ndb_no\":17008,\n  \"folate_total\":17.0,\n  \"lutein_zeaxanthin\":4.3,\n  \"iron\":2.14,\n  \"id\":5057,\n  \"energy_in_kcal\":243.0,\n  \"fat_mono_unsaturated\":5.68,\n  \"description\":\"LAMB,DOM,FORESHANK,LN&FAT,1\\/4\\\"FAT,CHOIC,CKD,BRSD\",\n  \"refuse_percent\":56.0,\n  \"food_folate\":17.0,\n  \"manganese\":0.025,\n  \"zinc\":7.69,\n  \"riboflavin\":0.19,\n  \"copper\":0.123},\n \"6914\":\n {\"beta_carotene\":-1.0,\n  \"lycopene\":1.09,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":1.349,\n  \"phosphorus\":523.0,\n  \"beta_crytoxanthin\":-1.0,\n  \"thiamin\":0.763,\n  \"selenium\":-1.0,\n  \"vitamin_k\":-1.0,\n  \"alpha_carotene\":-1.0,\n  \"carbohydrate\":66.27,\n  \"sugar\":-1.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":429.0,\n  \"fat_poly_unsaturated\":2.535,\n  \"lipid_total\":6.9,\n  \"prompt\":\"OATS -- 1 cup\",\n  \"vitamin_e\":-1.0,\n  \"vitamin_b6\":0.119,\n  \"fiber\":10.6,\n  \"vitamin_c\":0.0,\n  \"protein\":16.89,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.961,\n  \"fat_saturated\":1.217,\n  \"weight1_gram\":156.0,\n  \"folate_dfe\":56.0,\n  \"choline\":-1.0,\n  \"sodium\":2.0,\n  \"magnesium\":177.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":54.0,\n  \"ndb_no\":20038,\n  \"folate_total\":56.0,\n  \"lutein_zeaxanthin\":-1.0,\n  \"iron\":4.72,\n  \"id\":6914,\n  \"energy_in_kcal\":389.0,\n  \"fat_mono_unsaturated\":2.178,\n  \"description\":\"OATS\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":56.0,\n  \"manganese\":4.916,\n  \"zinc\":3.97,\n  \"riboflavin\":0.139,\n  \"copper\":0.626},\n \"3363\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.203,\n  \"phosphorus\":34.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.025,\n  \"selenium\":0.7,\n  \"vitamin_k\":0.1,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":17.77,\n  \"sugar\":1.7,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":415.0,\n  \"fat_poly_unsaturated\":0.154,\n  \"lipid_total\":0.75,\n  \"prompt\":\"GINGER ROOT,RAW -- 1 tsp\",\n  \"vitamin_e\":0.26,\n  \"vitamin_b6\":0.16,\n  \"fiber\":2.0,\n  \"vitamin_c\":5.0,\n  \"protein\":1.82,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.75,\n  \"fat_saturated\":0.203,\n  \"weight1_gram\":2.0,\n  \"folate_dfe\":11.0,\n  \"choline\":28.8,\n  \"sodium\":13.0,\n  \"magnesium\":43.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 tsp\",\n  \"calcium\":16.0,\n  \"ndb_no\":11216,\n  \"folate_total\":11.0,\n  \"lutein_zeaxanthin\":0.1,\n  \"iron\":0.6,\n  \"id\":3363,\n  \"energy_in_kcal\":80.0,\n  \"fat_mono_unsaturated\":0.154,\n  \"description\":\"GINGER ROOT,RAW\",\n  \"refuse_percent\":7.0,\n  \"food_folate\":11.0,\n  \"manganese\":0.229,\n  \"zinc\":0.34,\n  \"riboflavin\":0.034,\n  \"copper\":0.226},\n \"3555\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":1.497,\n  \"phosphorus\":86.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.081,\n  \"selenium\":9.3,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":3.28,\n  \"sugar\":1.65,\n  \"vitamin_b12\":0.04,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":318.0,\n  \"fat_poly_unsaturated\":0.139,\n  \"lipid_total\":0.34,\n  \"prompt\":\"MUSHROOMS,WHITE,RAW -- 1 cup, pieces or slices\",\n  \"vitamin_e\":0.01,\n  \"vitamin_b6\":0.104,\n  \"fiber\":1.0,\n  \"vitamin_c\":2.1,\n  \"protein\":3.09,\n  \"vitamin_a\":0.0,\n  \"niacin\":3.607,\n  \"fat_saturated\":0.043,\n  \"weight1_gram\":70.0,\n  \"folate_dfe\":16.0,\n  \"choline\":17.3,\n  \"sodium\":5.0,\n  \"magnesium\":9.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, pieces or slices\",\n  \"calcium\":3.0,\n  \"ndb_no\":11260,\n  \"folate_total\":16.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":0.5,\n  \"id\":3555,\n  \"energy_in_kcal\":22.0,\n  \"fat_mono_unsaturated\":0.0,\n  \"description\":\"MUSHROOMS,WHITE,RAW\",\n  \"refuse_percent\":3.0,\n  \"food_folate\":16.0,\n  \"manganese\":0.047,\n  \"zinc\":0.52,\n  \"riboflavin\":0.402,\n  \"copper\":0.318},\n \"1859\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":3.0,\n  \"pantothenic_acid\":0.061,\n  \"phosphorus\":11.0,\n  \"beta_crytoxanthin\":29.0,\n  \"thiamin\":0.017,\n  \"selenium\":0.0,\n  \"vitamin_k\":2.2,\n  \"alpha_carotene\":11.0,\n  \"carbohydrate\":13.81,\n  \"sugar\":10.39,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":107.0,\n  \"fat_poly_unsaturated\":0.051,\n  \"lipid_total\":0.17,\n  \"prompt\":\"APPLES,RAW,WITH SKIN -- 1 cup, quartered or chopped\",\n  \"vitamin_e\":0.18,\n  \"vitamin_b6\":0.041,\n  \"fiber\":2.4,\n  \"vitamin_c\":4.6,\n  \"protein\":0.26,\n  \"vitamin_a\":54.0,\n  \"niacin\":0.091,\n  \"fat_saturated\":0.028,\n  \"weight1_gram\":125.0,\n  \"folate_dfe\":3.0,\n  \"choline\":3.4,\n  \"sodium\":1.0,\n  \"magnesium\":5.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, quartered or chopped\",\n  \"calcium\":6.0,\n  \"ndb_no\":9003,\n  \"folate_total\":3.0,\n  \"lutein_zeaxanthin\":2.2,\n  \"iron\":0.12,\n  \"id\":1859,\n  \"energy_in_kcal\":52.0,\n  \"fat_mono_unsaturated\":0.007,\n  \"description\":\"APPLES,RAW,WITH SKIN\",\n  \"refuse_percent\":10.0,\n  \"food_folate\":3.0,\n  \"manganese\":0.035,\n  \"zinc\":0.04,\n  \"riboflavin\":0.026,\n  \"copper\":0.027},\n \"292\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":684.0,\n  \"pantothenic_acid\":0.11,\n  \"phosphorus\":24.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.005,\n  \"selenium\":1.0,\n  \"vitamin_k\":7.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.06,\n  \"sugar\":0.06,\n  \"vitamin_b12\":0.17,\n  \"cholesterol\":215.0,\n  \"retinol\":671.0,\n  \"potassium\":24.0,\n  \"fat_poly_unsaturated\":3.043,\n  \"lipid_total\":81.11,\n  \"prompt\":\"BUTTER,WITH SALT -- 1 cup\",\n  \"vitamin_e\":2.32,\n  \"vitamin_b6\":0.003,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":0.85,\n  \"vitamin_a\":2499.0,\n  \"niacin\":0.042,\n  \"fat_saturated\":51.368,\n  \"weight1_gram\":227.0,\n  \"folate_dfe\":3.0,\n  \"choline\":18.8,\n  \"sodium\":576.0,\n  \"magnesium\":2.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":24.0,\n  \"ndb_no\":1001,\n  \"folate_total\":3.0,\n  \"lutein_zeaxanthin\":7.0,\n  \"iron\":0.02,\n  \"id\":292,\n  \"energy_in_kcal\":717.0,\n  \"fat_mono_unsaturated\":21.021,\n  \"description\":\"BUTTER,WITH SALT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":3.0,\n  \"manganese\":0.0,\n  \"zinc\":0.09,\n  \"riboflavin\":0.034,\n  \"copper\":0.0},\n \"3652\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.098,\n  \"phosphorus\":27.0,\n  \"beta_crytoxanthin\":6.0,\n  \"thiamin\":0.041,\n  \"selenium\":0.5,\n  \"vitamin_k\":0.3,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":7.55,\n  \"sugar\":5.02,\n  \"vitamin_b12\":-1.0,\n  \"cholesterol\":0.0,\n  \"retinol\":-1.0,\n  \"potassium\":119.0,\n  \"fat_poly_unsaturated\":-1.0,\n  \"lipid_total\":0.08,\n  \"prompt\":\"ONIONS,SWT,RAW -- 1 onion\",\n  \"vitamin_e\":0.02,\n  \"vitamin_b6\":0.13,\n  \"fiber\":0.9,\n  \"vitamin_c\":4.8,\n  \"protein\":0.8,\n  \"vitamin_a\":1.0,\n  \"niacin\":0.133,\n  \"fat_saturated\":-1.0,\n  \"weight1_gram\":331.48,\n  \"folate_dfe\":-1.0,\n  \"choline\":5.5,\n  \"sodium\":8.0,\n  \"magnesium\":9.0,\n  \"folic_acid\":-1.0,\n  \"weight1_description\":\"1 onion\",\n  \"calcium\":20.0,\n  \"ndb_no\":11294,\n  \"folate_total\":23.0,\n  \"lutein_zeaxanthin\":0.3,\n  \"iron\":0.26,\n  \"id\":3652,\n  \"energy_in_kcal\":32.0,\n  \"fat_mono_unsaturated\":-1.0,\n  \"description\":\"ONIONS,SWT,RAW\",\n  \"refuse_percent\":16.0,\n  \"food_folate\":23.0,\n  \"manganese\":0.076,\n  \"zinc\":0.13,\n  \"riboflavin\":0.02,\n  \"copper\":0.056},\n \"1988\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":3.0,\n  \"pantothenic_acid\":0.124,\n  \"phosphorus\":12.0,\n  \"beta_crytoxanthin\":80.0,\n  \"thiamin\":0.037,\n  \"selenium\":0.1,\n  \"vitamin_k\":19.3,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":14.49,\n  \"sugar\":9.96,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":77.0,\n  \"fat_poly_unsaturated\":0.146,\n  \"lipid_total\":0.33,\n  \"prompt\":\"BLUEBERRIES,RAW -- 1 cup\",\n  \"vitamin_e\":0.57,\n  \"vitamin_b6\":0.052,\n  \"fiber\":2.4,\n  \"vitamin_c\":9.7,\n  \"protein\":0.74,\n  \"vitamin_a\":54.0,\n  \"niacin\":0.418,\n  \"fat_saturated\":0.028,\n  \"weight1_gram\":147.72,\n  \"folate_dfe\":6.0,\n  \"choline\":6.0,\n  \"sodium\":1.0,\n  \"magnesium\":6.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":6.0,\n  \"ndb_no\":9050,\n  \"folate_total\":6.0,\n  \"lutein_zeaxanthin\":19.3,\n  \"iron\":0.28,\n  \"id\":1988,\n  \"energy_in_kcal\":57.0,\n  \"fat_mono_unsaturated\":0.047,\n  \"description\":\"BLUEBERRIES,RAW\",\n  \"refuse_percent\":5.0,\n  \"food_folate\":6.0,\n  \"manganese\":0.336,\n  \"zinc\":0.16,\n  \"riboflavin\":0.041,\n  \"copper\":0.057},\n \"3109\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.229,\n  \"phosphorus\":489.0,\n  \"beta_crytoxanthin\":1.0,\n  \"thiamin\":0.074,\n  \"selenium\":2.8,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":19.29,\n  \"sugar\":4.9,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":746.0,\n  \"fat_poly_unsaturated\":12.649,\n  \"lipid_total\":52.83,\n  \"prompt\":\"ALMONDS,DRY RSTD,W\\/SALT -- 1 cup, whole kernels\",\n  \"vitamin_e\":26.0,\n  \"vitamin_b6\":0.126,\n  \"fiber\":11.8,\n  \"vitamin_c\":0.0,\n  \"protein\":22.09,\n  \"vitamin_a\":1.0,\n  \"niacin\":3.85,\n  \"fat_saturated\":4.047,\n  \"weight1_gram\":138.0,\n  \"folate_dfe\":33.0,\n  \"choline\":52.1,\n  \"sodium\":339.0,\n  \"magnesium\":286.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, whole kernels\",\n  \"calcium\":266.0,\n  \"ndb_no\":12563,\n  \"folate_total\":33.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":4.51,\n  \"id\":3109,\n  \"energy_in_kcal\":597.0,\n  \"fat_mono_unsaturated\":33.658,\n  \"description\":\"ALMONDS,DRY RSTD,W\\/SALT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":33.0,\n  \"manganese\":2.62,\n  \"zinc\":3.54,\n  \"riboflavin\":0.859,\n  \"copper\":1.17},\n \"1509\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":31.0,\n  \"pantothenic_acid\":0.573,\n  \"phosphorus\":66.0,\n  \"beta_crytoxanthin\":1403.0,\n  \"thiamin\":0.071,\n  \"selenium\":2.5,\n  \"vitamin_k\":101.6,\n  \"alpha_carotene\":1.0,\n  \"carbohydrate\":6.64,\n  \"sugar\":1.7,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":316.0,\n  \"fat_poly_unsaturated\":0.038,\n  \"lipid_total\":0.37,\n  \"prompt\":\"BROCCOLI,RAW -- 1 cup, chopped\",\n  \"vitamin_e\":0.78,\n  \"vitamin_b6\":0.175,\n  \"fiber\":2.6,\n  \"vitamin_c\":89.2,\n  \"protein\":2.82,\n  \"vitamin_a\":623.0,\n  \"niacin\":0.639,\n  \"fat_saturated\":0.039,\n  \"weight1_gram\":90.93,\n  \"folate_dfe\":63.0,\n  \"choline\":18.7,\n  \"sodium\":33.0,\n  \"magnesium\":21.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, chopped\",\n  \"calcium\":47.0,\n  \"ndb_no\":11090,\n  \"folate_total\":63.0,\n  \"lutein_zeaxanthin\":101.6,\n  \"iron\":0.73,\n  \"id\":1509,\n  \"energy_in_kcal\":34.0,\n  \"fat_mono_unsaturated\":0.011,\n  \"description\":\"BROCCOLI,RAW\",\n  \"refuse_percent\":39.0,\n  \"food_folate\":63.0,\n  \"manganese\":0.21,\n  \"zinc\":0.41,\n  \"riboflavin\":0.117,\n  \"copper\":0.049},\n \"3845\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":1.0,\n  \"pantothenic_acid\":0.341,\n  \"phosphorus\":72.0,\n  \"beta_crytoxanthin\":31.0,\n  \"thiamin\":0.072,\n  \"selenium\":0.8,\n  \"vitamin_k\":2.8,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":19.59,\n  \"sugar\":1.43,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":545.0,\n  \"fat_poly_unsaturated\":0.042,\n  \"lipid_total\":0.15,\n  \"prompt\":\n  \"POTATOES,RED,FLESH & SKN,BKD -- 1 potato, large (3\\\" to 4-1\\/4\\\" dia.\",\n  \"vitamin_e\":0.04,\n  \"vitamin_b6\":0.212,\n  \"fiber\":1.8,\n  \"vitamin_c\":12.6,\n  \"protein\":2.3,\n  \"vitamin_a\":10.0,\n  \"niacin\":1.595,\n  \"fat_saturated\":0.025,\n  \"weight1_gram\":298.9,\n  \"folate_dfe\":27.0,\n  \"choline\":18.9,\n  \"sodium\":12.0,\n  \"magnesium\":28.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 potato, large (3\\\" to 4-1\\/4\\\" dia.\",\n  \"calcium\":9.0,\n  \"ndb_no\":11358,\n  \"folate_total\":27.0,\n  \"lutein_zeaxanthin\":2.8,\n  \"iron\":0.7,\n  \"id\":3845,\n  \"energy_in_kcal\":89.0,\n  \"fat_mono_unsaturated\":0.002,\n  \"description\":\"POTATOES,RED,FLESH & SKN,BKD\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":27.0,\n  \"manganese\":0.173,\n  \"zinc\":0.4,\n  \"riboflavin\":0.05,\n  \"copper\":0.174},\n \"6981\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.135,\n  \"phosphorus\":54.0,\n  \"beta_crytoxanthin\":56.0,\n  \"thiamin\":0.083,\n  \"selenium\":8.6,\n  \"vitamin_k\":0.8,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":28.22,\n  \"sugar\":0.28,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":93.0,\n  \"fat_poly_unsaturated\":0.214,\n  \"lipid_total\":0.44,\n  \"prompt\":\"BARLEY,PEARLED,COOKED -- 1 cup\",\n  \"vitamin_e\":0.01,\n  \"vitamin_b6\":0.115,\n  \"fiber\":3.8,\n  \"vitamin_c\":0.0,\n  \"protein\":2.26,\n  \"vitamin_a\":7.0,\n  \"niacin\":2.063,\n  \"fat_saturated\":0.093,\n  \"weight1_gram\":157.0,\n  \"folate_dfe\":16.0,\n  \"choline\":13.4,\n  \"sodium\":3.0,\n  \"magnesium\":22.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":11.0,\n  \"ndb_no\":20006,\n  \"folate_total\":16.0,\n  \"lutein_zeaxanthin\":0.8,\n  \"iron\":1.33,\n  \"id\":6981,\n  \"energy_in_kcal\":123.0,\n  \"fat_mono_unsaturated\":0.057,\n  \"description\":\"BARLEY,PEARLED,COOKED\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":16.0,\n  \"manganese\":0.259,\n  \"zinc\":0.82,\n  \"riboflavin\":0.062,\n  \"copper\":0.105},\n \"4102\":\n {\"beta_carotene\":-1.0,\n  \"lycopene\":-1.0,\n  \"vitamin_a_rae\":25.0,\n  \"pantothenic_acid\":0.367,\n  \"phosphorus\":93.0,\n  \"beta_crytoxanthin\":-1.0,\n  \"thiamin\":0.042,\n  \"selenium\":0.3,\n  \"vitamin_k\":-1.0,\n  \"alpha_carotene\":-1.0,\n  \"carbohydrate\":3.11,\n  \"sugar\":-1.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":459.0,\n  \"fat_poly_unsaturated\":0.169,\n  \"lipid_total\":0.4,\n  \"prompt\":\"SQUASH,ZUCCHINI,BABY,RAW -- 1 large\",\n  \"vitamin_e\":-1.0,\n  \"vitamin_b6\":0.142,\n  \"fiber\":1.1,\n  \"vitamin_c\":34.1,\n  \"protein\":2.71,\n  \"vitamin_a\":490.0,\n  \"niacin\":0.705,\n  \"fat_saturated\":0.083,\n  \"weight1_gram\":16.0,\n  \"folate_dfe\":20.0,\n  \"choline\":-1.0,\n  \"sodium\":3.0,\n  \"magnesium\":33.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 large\",\n  \"calcium\":21.0,\n  \"ndb_no\":11953,\n  \"folate_total\":20.0,\n  \"lutein_zeaxanthin\":-1.0,\n  \"iron\":0.79,\n  \"id\":4102,\n  \"energy_in_kcal\":21.0,\n  \"fat_mono_unsaturated\":0.031,\n  \"description\":\"SQUASH,ZUCCHINI,BABY,RAW\",\n  \"refuse_percent\":13.0,\n  \"food_folate\":20.0,\n  \"manganese\":0.196,\n  \"zinc\":0.83,\n  \"riboflavin\":0.036,\n  \"copper\":0.097},\n \"6246\":\n {\"beta_carotene\":-1.0,\n  \"lycopene\":-1.0,\n  \"vitamin_a_rae\":-1.0,\n  \"pantothenic_acid\":-1.0,\n  \"phosphorus\":378.0,\n  \"beta_crytoxanthin\":-1.0,\n  \"thiamin\":0.96,\n  \"selenium\":-1.0,\n  \"vitamin_k\":-1.0,\n  \"alpha_carotene\":-1.0,\n  \"carbohydrate\":67.9,\n  \"sugar\":10.0,\n  \"vitamin_b12\":-1.0,\n  \"cholesterol\":14.0,\n  \"retinol\":-1.0,\n  \"potassium\":423.0,\n  \"fat_poly_unsaturated\":-1.0,\n  \"lipid_total\":3.7,\n  \"prompt\":\n  \"KRAFT MACARONI & CHEESE DINNER ORIGINAL FLAVOR,UNPREP -- 1 NLEA Serving,  (makes about 1 cup prepared)\",\n  \"vitamin_e\":-1.0,\n  \"vitamin_b6\":-1.0,\n  \"fiber\":2.1,\n  \"vitamin_c\":0.5,\n  \"protein\":16.2,\n  \"vitamin_a\":72.0,\n  \"niacin\":6.48,\n  \"fat_saturated\":1.8,\n  \"weight1_gram\":70.0,\n  \"folate_dfe\":-1.0,\n  \"choline\":-1.0,\n  \"sodium\":802.0,\n  \"magnesium\":-1.0,\n  \"folic_acid\":-1.0,\n  \"weight1_description\":\n  \"1 NLEA Serving,  (makes about 1 cup prepared)\",\n  \"calcium\":132.0,\n  \"ndb_no\":22005,\n  \"folate_total\":93.0,\n  \"lutein_zeaxanthin\":-1.0,\n  \"iron\":3.66,\n  \"id\":6246,\n  \"energy_in_kcal\":370.0,\n  \"fat_mono_unsaturated\":-1.0,\n  \"description\":\n  \"KRAFT MACARONI & CHEESE DINNER ORIGINAL FLAVOR,UNPREP\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":-1.0,\n  \"manganese\":-1.0,\n  \"zinc\":-1.0,\n  \"riboflavin\":0.59,\n  \"copper\":-1.0},\n \"390\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":137.0,\n  \"pantothenic_acid\":0.09,\n  \"phosphorus\":524.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.101,\n  \"selenium\":16.3,\n  \"vitamin_k\":1.3,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":3.83,\n  \"sugar\":0.6,\n  \"vitamin_b12\":2.31,\n  \"cholesterol\":54.0,\n  \"retinol\":133.0,\n  \"potassium\":95.0,\n  \"fat_poly_unsaturated\":0.626,\n  \"lipid_total\":20.03,\n  \"prompt\":\n  \"CHEESE,MOZZARELLA,PART SKIM MILK,LO MOIST -- 1 cup, diced\",\n  \"vitamin_e\":0.37,\n  \"vitamin_b6\":0.079,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":25.96,\n  \"vitamin_a\":517.0,\n  \"niacin\":0.119,\n  \"fat_saturated\":12.67,\n  \"weight1_gram\":132.0,\n  \"folate_dfe\":10.0,\n  \"choline\":14.2,\n  \"sodium\":528.0,\n  \"magnesium\":26.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, diced\",\n  \"calcium\":731.0,\n  \"ndb_no\":1029,\n  \"folate_total\":10.0,\n  \"lutein_zeaxanthin\":1.3,\n  \"iron\":0.25,\n  \"id\":390,\n  \"energy_in_kcal\":302.0,\n  \"fat_mono_unsaturated\":5.73,\n  \"description\":\"CHEESE,MOZZARELLA,PART SKIM MILK,LO MOIST\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":10.0,\n  \"manganese\":0.011,\n  \"zinc\":3.13,\n  \"riboflavin\":0.329,\n  \"copper\":0.027},\n \"1574\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":38.0,\n  \"pantothenic_acid\":0.309,\n  \"phosphorus\":69.0,\n  \"beta_crytoxanthin\":1590.0,\n  \"thiamin\":0.139,\n  \"selenium\":1.6,\n  \"vitamin_k\":177.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":8.95,\n  \"sugar\":2.2,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":389.0,\n  \"fat_poly_unsaturated\":0.153,\n  \"lipid_total\":0.3,\n  \"prompt\":\"BRUSSELS SPROUTS,RAW -- 1 cup\",\n  \"vitamin_e\":0.88,\n  \"vitamin_b6\":0.219,\n  \"fiber\":3.8,\n  \"vitamin_c\":85.0,\n  \"protein\":3.38,\n  \"vitamin_a\":754.0,\n  \"niacin\":0.745,\n  \"fat_saturated\":0.062,\n  \"weight1_gram\":88.0,\n  \"folate_dfe\":61.0,\n  \"choline\":19.1,\n  \"sodium\":25.0,\n  \"magnesium\":23.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":42.0,\n  \"ndb_no\":11098,\n  \"folate_total\":61.0,\n  \"lutein_zeaxanthin\":177.0,\n  \"iron\":1.4,\n  \"id\":1574,\n  \"energy_in_kcal\":43.0,\n  \"fat_mono_unsaturated\":0.023,\n  \"description\":\"BRUSSELS SPROUTS,RAW\",\n  \"refuse_percent\":10.0,\n  \"food_folate\":61.0,\n  \"manganese\":0.337,\n  \"zinc\":0.42,\n  \"riboflavin\":0.09,\n  \"copper\":0.07},\n \"3079\":\n {\"beta_carotene\":-1.0,\n  \"lycopene\":-1.0,\n  \"vitamin_a_rae\":75.0,\n  \"pantothenic_acid\":0.186,\n  \"phosphorus\":29.0,\n  \"beta_crytoxanthin\":-1.0,\n  \"thiamin\":0.046,\n  \"selenium\":0.4,\n  \"vitamin_k\":-1.0,\n  \"alpha_carotene\":-1.0,\n  \"carbohydrate\":3.18,\n  \"sugar\":-1.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":212.0,\n  \"fat_poly_unsaturated\":0.076,\n  \"lipid_total\":0.19,\n  \"prompt\":\"TOMATOES,ORANGE,RAW -- 1 cup, chopped\",\n  \"vitamin_e\":-1.0,\n  \"vitamin_b6\":0.06,\n  \"fiber\":0.9,\n  \"vitamin_c\":16.0,\n  \"protein\":1.16,\n  \"vitamin_a\":1496.0,\n  \"niacin\":0.593,\n  \"fat_saturated\":0.025,\n  \"weight1_gram\":158.0,\n  \"folate_dfe\":29.0,\n  \"choline\":-1.0,\n  \"sodium\":42.0,\n  \"magnesium\":8.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, chopped\",\n  \"calcium\":5.0,\n  \"ndb_no\":11695,\n  \"folate_total\":29.0,\n  \"lutein_zeaxanthin\":-1.0,\n  \"iron\":0.47,\n  \"id\":3079,\n  \"energy_in_kcal\":16.0,\n  \"fat_mono_unsaturated\":0.028,\n  \"description\":\"TOMATOES,ORANGE,RAW\",\n  \"refuse_percent\":9.0,\n  \"food_folate\":29.0,\n  \"manganese\":0.088,\n  \"zinc\":0.14,\n  \"riboflavin\":0.034,\n  \"copper\":0.062},\n \"295\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":198.0,\n  \"pantothenic_acid\":1.729,\n  \"phosphorus\":387.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.029,\n  \"selenium\":14.5,\n  \"vitamin_k\":2.4,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":2.34,\n  \"sugar\":0.5,\n  \"vitamin_b12\":1.22,\n  \"cholesterol\":75.0,\n  \"retinol\":192.0,\n  \"potassium\":256.0,\n  \"fat_poly_unsaturated\":0.8,\n  \"lipid_total\":28.74,\n  \"prompt\":\"CHEESE,BLUE -- 1 oz\",\n  \"vitamin_e\":0.25,\n  \"vitamin_b6\":0.166,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":21.4,\n  \"vitamin_a\":763.0,\n  \"niacin\":1.016,\n  \"fat_saturated\":18.669,\n  \"weight1_gram\":28.35,\n  \"folate_dfe\":36.0,\n  \"choline\":15.4,\n  \"sodium\":1395.0,\n  \"magnesium\":23.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 oz\",\n  \"calcium\":528.0,\n  \"ndb_no\":1004,\n  \"folate_total\":36.0,\n  \"lutein_zeaxanthin\":2.4,\n  \"iron\":0.31,\n  \"id\":295,\n  \"energy_in_kcal\":353.0,\n  \"fat_mono_unsaturated\":7.778,\n  \"description\":\"CHEESE,BLUE\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":36.0,\n  \"manganese\":0.009,\n  \"zinc\":2.66,\n  \"riboflavin\":0.382,\n  \"copper\":0.04},\n \"487\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":279.0,\n  \"pantothenic_acid\":0.259,\n  \"phosphorus\":61.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.024,\n  \"selenium\":0.5,\n  \"vitamin_k\":2.7,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":2.96,\n  \"sugar\":0.11,\n  \"vitamin_b12\":0.2,\n  \"cholesterol\":111.0,\n  \"retinol\":274.0,\n  \"potassium\":97.0,\n  \"fat_poly_unsaturated\":0.884,\n  \"lipid_total\":30.91,\n  \"prompt\":\"CREAM,FLUID,LT WHIPPING -- 1 cup,  whipped\",\n  \"vitamin_e\":0.88,\n  \"vitamin_b6\":0.028,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.6,\n  \"protein\":2.17,\n  \"vitamin_a\":1013.0,\n  \"niacin\":0.042,\n  \"fat_saturated\":19.337,\n  \"weight1_gram\":120.0,\n  \"folate_dfe\":4.0,\n  \"choline\":16.8,\n  \"sodium\":34.0,\n  \"magnesium\":7.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup,  whipped\",\n  \"calcium\":69.0,\n  \"ndb_no\":1052,\n  \"folate_total\":4.0,\n  \"lutein_zeaxanthin\":2.7,\n  \"iron\":0.03,\n  \"id\":487,\n  \"energy_in_kcal\":292.0,\n  \"fat_mono_unsaturated\":9.093,\n  \"description\":\"CREAM,FLUID,LT WHIPPING\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":4.0,\n  \"manganese\":0.001,\n  \"zinc\":0.25,\n  \"riboflavin\":0.125,\n  \"copper\":0.007},\n \"3655\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":421.0,\n  \"pantothenic_acid\":0.4,\n  \"phosphorus\":58.0,\n  \"beta_crytoxanthin\":5561.0,\n  \"thiamin\":0.086,\n  \"selenium\":0.1,\n  \"vitamin_k\":1640.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":6.33,\n  \"sugar\":0.85,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":554.0,\n  \"fat_poly_unsaturated\":0.124,\n  \"lipid_total\":0.79,\n  \"prompt\":\"PARSLEY,RAW -- 1 cup\",\n  \"vitamin_e\":0.75,\n  \"vitamin_b6\":0.09,\n  \"fiber\":3.3,\n  \"vitamin_c\":133.0,\n  \"protein\":2.97,\n  \"vitamin_a\":8424.0,\n  \"niacin\":1.313,\n  \"fat_saturated\":0.132,\n  \"weight1_gram\":60.0,\n  \"folate_dfe\":152.0,\n  \"choline\":12.8,\n  \"sodium\":56.0,\n  \"magnesium\":50.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":138.0,\n  \"ndb_no\":11297,\n  \"folate_total\":152.0,\n  \"lutein_zeaxanthin\":1640.0,\n  \"iron\":6.2,\n  \"id\":3655,\n  \"energy_in_kcal\":36.0,\n  \"fat_mono_unsaturated\":0.295,\n  \"description\":\"PARSLEY,RAW\",\n  \"refuse_percent\":5.0,\n  \"food_folate\":152.0,\n  \"manganese\":0.16,\n  \"zinc\":1.07,\n  \"riboflavin\":0.098,\n  \"copper\":0.149},\n \"7079\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.431,\n  \"phosphorus\":189.0,\n  \"beta_crytoxanthin\":18.0,\n  \"thiamin\":0.891,\n  \"selenium\":63.2,\n  \"vitamin_k\":0.1,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":74.67,\n  \"sugar\":2.67,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":223.0,\n  \"fat_poly_unsaturated\":0.564,\n  \"lipid_total\":1.51,\n  \"prompt\":\"MACARONI,DRY,ENR -- 1 cup, elbow shaped\",\n  \"vitamin_e\":0.11,\n  \"vitamin_b6\":0.142,\n  \"fiber\":3.2,\n  \"vitamin_c\":0.0,\n  \"protein\":13.04,\n  \"vitamin_a\":0.0,\n  \"niacin\":7.177,\n  \"fat_saturated\":0.277,\n  \"weight1_gram\":105.0,\n  \"folate_dfe\":391.0,\n  \"choline\":15.1,\n  \"sodium\":6.0,\n  \"magnesium\":53.0,\n  \"folic_acid\":219.0,\n  \"weight1_description\":\"1 cup, elbow shaped\",\n  \"calcium\":21.0,\n  \"ndb_no\":20099,\n  \"folate_total\":237.0,\n  \"lutein_zeaxanthin\":0.1,\n  \"iron\":3.3,\n  \"id\":7079,\n  \"energy_in_kcal\":371.0,\n  \"fat_mono_unsaturated\":0.171,\n  \"description\":\"MACARONI,DRY,ENR\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":18.0,\n  \"manganese\":0.917,\n  \"zinc\":1.41,\n  \"riboflavin\":0.4,\n  \"copper\":0.289},\n \"4071\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.165,\n  \"phosphorus\":20.0,\n  \"beta_crytoxanthin\":10.0,\n  \"thiamin\":0.012,\n  \"selenium\":0.6,\n  \"vitamin_k\":1.3,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":3.4,\n  \"sugar\":1.86,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":233.0,\n  \"fat_poly_unsaturated\":0.048,\n  \"lipid_total\":0.1,\n  \"prompt\":\"RADISHES,RAW -- 1 cup, slices\",\n  \"vitamin_e\":0.0,\n  \"vitamin_b6\":0.071,\n  \"fiber\":1.6,\n  \"vitamin_c\":14.8,\n  \"protein\":0.68,\n  \"vitamin_a\":7.0,\n  \"niacin\":0.254,\n  \"fat_saturated\":0.032,\n  \"weight1_gram\":116.0,\n  \"folate_dfe\":25.0,\n  \"choline\":6.5,\n  \"sodium\":39.0,\n  \"magnesium\":10.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, slices\",\n  \"calcium\":25.0,\n  \"ndb_no\":11429,\n  \"folate_total\":25.0,\n  \"lutein_zeaxanthin\":1.3,\n  \"iron\":0.34,\n  \"id\":4071,\n  \"energy_in_kcal\":16.0,\n  \"fat_mono_unsaturated\":0.017,\n  \"description\":\"RADISHES,RAW\",\n  \"refuse_percent\":10.0,\n  \"food_folate\":25.0,\n  \"manganese\":0.069,\n  \"zinc\":0.28,\n  \"riboflavin\":0.039,\n  \"copper\":0.05},\n \"3080\":\n {\"beta_carotene\":-1.0,\n  \"lycopene\":-1.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.11,\n  \"phosphorus\":36.0,\n  \"beta_crytoxanthin\":-1.0,\n  \"thiamin\":0.041,\n  \"selenium\":0.4,\n  \"vitamin_k\":-1.0,\n  \"alpha_carotene\":-1.0,\n  \"carbohydrate\":2.98,\n  \"sugar\":-1.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":258.0,\n  \"fat_poly_unsaturated\":0.108,\n  \"lipid_total\":0.26,\n  \"prompt\":\"TOMATOES,YELLOW,RAW -- 1 cup, chopped\",\n  \"vitamin_e\":-1.0,\n  \"vitamin_b6\":0.056,\n  \"fiber\":0.7,\n  \"vitamin_c\":9.0,\n  \"protein\":0.98,\n  \"vitamin_a\":0.0,\n  \"niacin\":1.179,\n  \"fat_saturated\":0.036,\n  \"weight1_gram\":139.0,\n  \"folate_dfe\":30.0,\n  \"choline\":-1.0,\n  \"sodium\":23.0,\n  \"magnesium\":12.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, chopped\",\n  \"calcium\":11.0,\n  \"ndb_no\":11696,\n  \"folate_total\":30.0,\n  \"lutein_zeaxanthin\":-1.0,\n  \"iron\":0.49,\n  \"id\":3080,\n  \"energy_in_kcal\":15.0,\n  \"fat_mono_unsaturated\":0.04,\n  \"description\":\"TOMATOES,YELLOW,RAW\",\n  \"refuse_percent\":9.0,\n  \"food_folate\":30.0,\n  \"manganese\":0.12,\n  \"zinc\":0.28,\n  \"riboflavin\":0.047,\n  \"copper\":0.101},\n \"1224\":\n {\"beta_carotene\":-1.0,\n  \"lycopene\":-1.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":-1.0,\n  \"phosphorus\":8.0,\n  \"beta_crytoxanthin\":-1.0,\n  \"thiamin\":-1.0,\n  \"selenium\":-1.0,\n  \"vitamin_k\":-1.0,\n  \"alpha_carotene\":-1.0,\n  \"carbohydrate\":0.27,\n  \"sugar\":0.0,\n  \"vitamin_b12\":-1.0,\n  \"cholesterol\":-1.0,\n  \"retinol\":-1.0,\n  \"potassium\":39.0,\n  \"fat_poly_unsaturated\":-1.0,\n  \"lipid_total\":0.0,\n  \"prompt\":\"VINEGAR,RED WINE -- 1 cup\",\n  \"vitamin_e\":-1.0,\n  \"vitamin_b6\":-1.0,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.5,\n  \"protein\":0.04,\n  \"vitamin_a\":0.0,\n  \"niacin\":-1.0,\n  \"fat_saturated\":0.0,\n  \"weight1_gram\":239.0,\n  \"folate_dfe\":-1.0,\n  \"choline\":-1.0,\n  \"sodium\":8.0,\n  \"magnesium\":4.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":6.0,\n  \"ndb_no\":2068,\n  \"folate_total\":-1.0,\n  \"lutein_zeaxanthin\":-1.0,\n  \"iron\":0.45,\n  \"id\":1224,\n  \"energy_in_kcal\":19.0,\n  \"fat_mono_unsaturated\":-1.0,\n  \"description\":\"VINEGAR,RED WINE\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":-1.0,\n  \"manganese\":0.046,\n  \"zinc\":0.03,\n  \"riboflavin\":-1.0,\n  \"copper\":0.01},\n \"3816\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.545,\n  \"phosphorus\":174.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.03,\n  \"selenium\":21.5,\n  \"vitamin_k\":2.3,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.0,\n  \"sugar\":0.0,\n  \"vitamin_b12\":2.65,\n  \"cholesterol\":84.0,\n  \"retinol\":0.0,\n  \"potassium\":305.0,\n  \"fat_poly_unsaturated\":0.636,\n  \"lipid_total\":21.83,\n  \"prompt\":\n  \"BEEF,GROUND,PATTIES,FRZ,CKD,BRLD -- 1 unit, cooked (yield from 1 lb raw meat)\",\n  \"vitamin_e\":0.0,\n  \"vitamin_b6\":0.435,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":23.05,\n  \"vitamin_a\":0.0,\n  \"niacin\":5.305,\n  \"fat_saturated\":8.805,\n  \"weight1_gram\":313.0,\n  \"folate_dfe\":21.0,\n  \"choline\":74.6,\n  \"sodium\":77.0,\n  \"magnesium\":19.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 unit, cooked (yield from 1 lb raw meat)\",\n  \"calcium\":11.0,\n  \"ndb_no\":13317,\n  \"folate_total\":21.0,\n  \"lutein_zeaxanthin\":2.3,\n  \"iron\":2.42,\n  \"id\":3816,\n  \"energy_in_kcal\":295.0,\n  \"fat_mono_unsaturated\":9.358,\n  \"description\":\"BEEF,GROUND,PATTIES,FRZ,CKD,BRLD\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":21.0,\n  \"manganese\":0.022,\n  \"zinc\":4.84,\n  \"riboflavin\":0.176,\n  \"copper\":0.077},\n \"1225\":\n {\"beta_carotene\":-1.0,\n  \"lycopene\":-1.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":-1.0,\n  \"phosphorus\":19.0,\n  \"beta_crytoxanthin\":-1.0,\n  \"thiamin\":-1.0,\n  \"selenium\":-1.0,\n  \"vitamin_k\":-1.0,\n  \"alpha_carotene\":-1.0,\n  \"carbohydrate\":17.03,\n  \"sugar\":14.95,\n  \"vitamin_b12\":-1.0,\n  \"cholesterol\":-1.0,\n  \"retinol\":-1.0,\n  \"potassium\":112.0,\n  \"fat_poly_unsaturated\":-1.0,\n  \"lipid_total\":0.0,\n  \"prompt\":\"VINEGAR,BALSAMIC -- 1 cup\",\n  \"vitamin_e\":-1.0,\n  \"vitamin_b6\":-1.0,\n  \"fiber\":-1.0,\n  \"vitamin_c\":0.0,\n  \"protein\":0.49,\n  \"vitamin_a\":0.0,\n  \"niacin\":-1.0,\n  \"fat_saturated\":0.0,\n  \"weight1_gram\":255.0,\n  \"folate_dfe\":-1.0,\n  \"choline\":-1.0,\n  \"sodium\":23.0,\n  \"magnesium\":12.0,\n  \"folic_acid\":-1.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":27.0,\n  \"ndb_no\":2069,\n  \"folate_total\":-1.0,\n  \"lutein_zeaxanthin\":-1.0,\n  \"iron\":0.72,\n  \"id\":1225,\n  \"energy_in_kcal\":88.0,\n  \"fat_mono_unsaturated\":-1.0,\n  \"description\":\"VINEGAR,BALSAMIC\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":-1.0,\n  \"manganese\":0.131,\n  \"zinc\":0.08,\n  \"riboflavin\":-1.0,\n  \"copper\":0.026},\n \"393\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":120.0,\n  \"pantothenic_acid\":0.325,\n  \"phosphorus\":729.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.029,\n  \"selenium\":17.7,\n  \"vitamin_k\":1.9,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":4.06,\n  \"sugar\":0.9,\n  \"vitamin_b12\":2.26,\n  \"cholesterol\":88.0,\n  \"retinol\":117.0,\n  \"potassium\":125.0,\n  \"fat_poly_unsaturated\":1.173,\n  \"lipid_total\":28.61,\n  \"prompt\":\"CHEESE,PARMESAN,GRATED -- 1 cup\",\n  \"vitamin_e\":0.26,\n  \"vitamin_b6\":0.049,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":38.46,\n  \"vitamin_a\":442.0,\n  \"niacin\":0.114,\n  \"fat_saturated\":17.301,\n  \"weight1_gram\":100.0,\n  \"folate_dfe\":10.0,\n  \"choline\":15.4,\n  \"sodium\":1529.0,\n  \"magnesium\":38.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":1109.0,\n  \"ndb_no\":1032,\n  \"folate_total\":10.0,\n  \"lutein_zeaxanthin\":1.9,\n  \"iron\":0.9,\n  \"id\":393,\n  \"energy_in_kcal\":431.0,\n  \"fat_mono_unsaturated\":8.375,\n  \"description\":\"CHEESE,PARMESAN,GRATED\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":10.0,\n  \"manganese\":0.085,\n  \"zinc\":3.87,\n  \"riboflavin\":0.486,\n  \"copper\":0.238},\n \"3753\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":18.0,\n  \"pantothenic_acid\":0.099,\n  \"phosphorus\":20.0,\n  \"beta_crytoxanthin\":341.0,\n  \"thiamin\":0.057,\n  \"selenium\":0.0,\n  \"vitamin_k\":7.4,\n  \"alpha_carotene\":7.0,\n  \"carbohydrate\":4.64,\n  \"sugar\":2.4,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":175.0,\n  \"fat_poly_unsaturated\":0.062,\n  \"lipid_total\":0.17,\n  \"prompt\":\"PEPPERS,SWT,GRN,RAW -- 1 cup, chopped\",\n  \"vitamin_e\":0.37,\n  \"vitamin_b6\":0.224,\n  \"fiber\":1.7,\n  \"vitamin_c\":80.4,\n  \"protein\":0.86,\n  \"vitamin_a\":370.0,\n  \"niacin\":0.48,\n  \"fat_saturated\":0.058,\n  \"weight1_gram\":149.0,\n  \"folate_dfe\":10.0,\n  \"choline\":5.5,\n  \"sodium\":3.0,\n  \"magnesium\":10.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, chopped\",\n  \"calcium\":10.0,\n  \"ndb_no\":11333,\n  \"folate_total\":10.0,\n  \"lutein_zeaxanthin\":7.4,\n  \"iron\":0.34,\n  \"id\":3753,\n  \"energy_in_kcal\":20.0,\n  \"fat_mono_unsaturated\":0.008,\n  \"description\":\"PEPPERS,SWT,GRN,RAW\",\n  \"refuse_percent\":18.0,\n  \"food_folate\":10.0,\n  \"manganese\":0.122,\n  \"zinc\":0.13,\n  \"riboflavin\":0.028,\n  \"copper\":0.066},\n \"298\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":241.0,\n  \"pantothenic_acid\":1.364,\n  \"phosphorus\":347.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.028,\n  \"selenium\":14.5,\n  \"vitamin_k\":2.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.46,\n  \"sugar\":0.46,\n  \"vitamin_b12\":1.3,\n  \"cholesterol\":72.0,\n  \"retinol\":240.0,\n  \"potassium\":187.0,\n  \"fat_poly_unsaturated\":0.724,\n  \"lipid_total\":24.26,\n  \"prompt\":\"CHEESE,CAMEMBERT -- 1 cup\",\n  \"vitamin_e\":0.21,\n  \"vitamin_b6\":0.227,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":19.8,\n  \"vitamin_a\":820.0,\n  \"niacin\":0.63,\n  \"fat_saturated\":15.259,\n  \"weight1_gram\":246.0,\n  \"folate_dfe\":62.0,\n  \"choline\":15.4,\n  \"sodium\":842.0,\n  \"magnesium\":20.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":388.0,\n  \"ndb_no\":1007,\n  \"folate_total\":62.0,\n  \"lutein_zeaxanthin\":2.0,\n  \"iron\":0.33,\n  \"id\":298,\n  \"energy_in_kcal\":300.0,\n  \"fat_mono_unsaturated\":7.023,\n  \"description\":\"CHEESE,CAMEMBERT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":62.0,\n  \"manganese\":0.038,\n  \"zinc\":2.38,\n  \"riboflavin\":0.488,\n  \"copper\":0.021},\n \"4458\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":7.0,\n  \"pantothenic_acid\":0.314,\n  \"phosphorus\":55.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.112,\n  \"selenium\":0.7,\n  \"vitamin_k\":2.3,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":27.88,\n  \"sugar\":0.5,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":816.0,\n  \"fat_poly_unsaturated\":0.076,\n  \"lipid_total\":0.17,\n  \"prompt\":\"YAM,RAW -- 1 cup, cubes\",\n  \"vitamin_e\":0.35,\n  \"vitamin_b6\":0.293,\n  \"fiber\":4.1,\n  \"vitamin_c\":17.1,\n  \"protein\":1.53,\n  \"vitamin_a\":138.0,\n  \"niacin\":0.552,\n  \"fat_saturated\":0.037,\n  \"weight1_gram\":150.0,\n  \"folate_dfe\":23.0,\n  \"choline\":16.5,\n  \"sodium\":9.0,\n  \"magnesium\":21.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, cubes\",\n  \"calcium\":17.0,\n  \"ndb_no\":11601,\n  \"folate_total\":23.0,\n  \"lutein_zeaxanthin\":2.3,\n  \"iron\":0.54,\n  \"id\":4458,\n  \"energy_in_kcal\":118.0,\n  \"fat_mono_unsaturated\":0.006,\n  \"description\":\"YAM,RAW\",\n  \"refuse_percent\":14.0,\n  \"food_folate\":23.0,\n  \"manganese\":0.397,\n  \"zinc\":0.24,\n  \"riboflavin\":0.032,\n  \"copper\":0.178},\n \"3722\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.314,\n  \"phosphorus\":176.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.101,\n  \"selenium\":13.9,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.0,\n  \"sugar\":0.0,\n  \"vitamin_b12\":2.79,\n  \"cholesterol\":61.0,\n  \"retinol\":0.0,\n  \"potassium\":281.0,\n  \"fat_poly_unsaturated\":0.55,\n  \"lipid_total\":15.41,\n  \"prompt\":\n  \"BEEF,SHRT LOIN,T-BONE STEAK,LN & FAT,1\\/4\\\" FAT,USDA CHOIC,RAW -- 1 lb\",\n  \"vitamin_e\":0.11,\n  \"vitamin_b6\":0.388,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":19.17,\n  \"vitamin_a\":0.0,\n  \"niacin\":3.67,\n  \"fat_saturated\":5.838,\n  \"weight1_gram\":453.6,\n  \"folate_dfe\":6.0,\n  \"choline\":-1.0,\n  \"sodium\":54.0,\n  \"magnesium\":20.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 lb\",\n  \"calcium\":4.0,\n  \"ndb_no\":13233,\n  \"folate_total\":6.0,\n  \"lutein_zeaxanthin\":-1.0,\n  \"iron\":3.29,\n  \"id\":3722,\n  \"energy_in_kcal\":221.0,\n  \"fat_mono_unsaturated\":6.7,\n  \"description\":\n  \"BEEF,SHRT LOIN,T-BONE STEAK,LN & FAT,1\\/4\\\" FAT,USDA CHOIC,RAW\",\n  \"refuse_percent\":22.0,\n  \"food_folate\":6.0,\n  \"manganese\":0.013,\n  \"zinc\":3.32,\n  \"riboflavin\":0.175,\n  \"copper\":0.08},\n \"3882\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.76,\n  \"phosphorus\":89.0,\n  \"beta_crytoxanthin\":34.0,\n  \"thiamin\":0.2,\n  \"selenium\":0.6,\n  \"vitamin_k\":0.3,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":19.02,\n  \"sugar\":3.22,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":270.0,\n  \"fat_poly_unsaturated\":0.559,\n  \"lipid_total\":1.18,\n  \"prompt\":\n  \"CORN,SWEET,WHITE,RAW -- 1 ear,  small (5-1\\/2\\\" to 6-1\\/2\\\" long)\",\n  \"vitamin_e\":0.07,\n  \"vitamin_b6\":0.055,\n  \"fiber\":2.7,\n  \"vitamin_c\":6.8,\n  \"protein\":3.22,\n  \"vitamin_a\":1.0,\n  \"niacin\":1.7,\n  \"fat_saturated\":0.182,\n  \"weight1_gram\":73.0,\n  \"folate_dfe\":46.0,\n  \"choline\":-1.0,\n  \"sodium\":15.0,\n  \"magnesium\":37.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 ear,  small (5-1\\/2\\\" to 6-1\\/2\\\" long)\",\n  \"calcium\":2.0,\n  \"ndb_no\":11900,\n  \"folate_total\":46.0,\n  \"lutein_zeaxanthin\":0.3,\n  \"iron\":0.52,\n  \"id\":3882,\n  \"energy_in_kcal\":86.0,\n  \"fat_mono_unsaturated\":0.347,\n  \"description\":\"CORN,SWEET,WHITE,RAW\",\n  \"refuse_percent\":64.0,\n  \"food_folate\":46.0,\n  \"manganese\":0.161,\n  \"zinc\":0.45,\n  \"riboflavin\":0.06,\n  \"copper\":0.054},\n \"3243\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":218.0,\n  \"pantothenic_acid\":0.324,\n  \"phosphorus\":58.0,\n  \"beta_crytoxanthin\":323.0,\n  \"thiamin\":0.078,\n  \"selenium\":0.9,\n  \"vitamin_k\":212.7,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":4.35,\n  \"sugar\":1.85,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":296.0,\n  \"fat_poly_unsaturated\":0.267,\n  \"lipid_total\":0.73,\n  \"prompt\":\"CHIVES,RAW -- 1 tbsp, chopped\",\n  \"vitamin_e\":0.21,\n  \"vitamin_b6\":0.138,\n  \"fiber\":2.5,\n  \"vitamin_c\":58.1,\n  \"protein\":3.27,\n  \"vitamin_a\":4353.0,\n  \"niacin\":0.647,\n  \"fat_saturated\":0.146,\n  \"weight1_gram\":3.0,\n  \"folate_dfe\":105.0,\n  \"choline\":5.2,\n  \"sodium\":3.0,\n  \"magnesium\":42.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 tbsp, chopped\",\n  \"calcium\":92.0,\n  \"ndb_no\":11156,\n  \"folate_total\":105.0,\n  \"lutein_zeaxanthin\":212.7,\n  \"iron\":1.6,\n  \"id\":3243,\n  \"energy_in_kcal\":30.0,\n  \"fat_mono_unsaturated\":0.095,\n  \"description\":\"CHIVES,RAW\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":105.0,\n  \"manganese\":0.373,\n  \"zinc\":0.56,\n  \"riboflavin\":0.115,\n  \"copper\":0.157},\n \"491\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":177.0,\n  \"pantothenic_acid\":0.36,\n  \"phosphorus\":85.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.035,\n  \"selenium\":2.2,\n  \"vitamin_k\":1.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":4.27,\n  \"sugar\":0.16,\n  \"vitamin_b12\":0.3,\n  \"cholesterol\":44.0,\n  \"retinol\":174.0,\n  \"potassium\":144.0,\n  \"fat_poly_unsaturated\":0.778,\n  \"lipid_total\":20.96,\n  \"prompt\":\"CREAM,SOUR,CULTURED -- 1 cup\",\n  \"vitamin_e\":0.6,\n  \"vitamin_b6\":0.016,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.9,\n  \"protein\":3.16,\n  \"vitamin_a\":648.0,\n  \"niacin\":0.067,\n  \"fat_saturated\":13.047,\n  \"weight1_gram\":230.0,\n  \"folate_dfe\":11.0,\n  \"choline\":19.2,\n  \"sodium\":53.0,\n  \"magnesium\":11.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":116.0,\n  \"ndb_no\":1056,\n  \"folate_total\":11.0,\n  \"lutein_zeaxanthin\":1.0,\n  \"iron\":0.06,\n  \"id\":491,\n  \"energy_in_kcal\":214.0,\n  \"fat_mono_unsaturated\":6.054,\n  \"description\":\"CREAM,SOUR,CULTURED\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":11.0,\n  \"manganese\":0.003,\n  \"zinc\":0.27,\n  \"riboflavin\":0.149,\n  \"copper\":0.019},\n \"3563\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":3.594,\n  \"phosphorus\":29.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.037,\n  \"selenium\":24.8,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":14.39,\n  \"sugar\":3.61,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":117.0,\n  \"fat_poly_unsaturated\":0.034,\n  \"lipid_total\":0.22,\n  \"prompt\":\"MUSHROOMS,SHIITAKE,CKD,WO\\/SALT -- 1 cup, pieces\",\n  \"vitamin_e\":0.03,\n  \"vitamin_b6\":0.159,\n  \"fiber\":2.1,\n  \"vitamin_c\":0.3,\n  \"protein\":1.56,\n  \"vitamin_a\":0.0,\n  \"niacin\":1.5,\n  \"fat_saturated\":0.05,\n  \"weight1_gram\":145.0,\n  \"folate_dfe\":21.0,\n  \"choline\":36.8,\n  \"sodium\":4.0,\n  \"magnesium\":14.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, pieces\",\n  \"calcium\":3.0,\n  \"ndb_no\":11269,\n  \"folate_total\":21.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":0.44,\n  \"id\":3563,\n  \"energy_in_kcal\":56.0,\n  \"fat_mono_unsaturated\":0.07,\n  \"description\":\"MUSHROOMS,SHIITAKE,CKD,WO\\/SALT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":21.0,\n  \"manganese\":0.204,\n  \"zinc\":1.33,\n  \"riboflavin\":0.17,\n  \"copper\":0.896},\n \"2763\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":8.0,\n  \"pantothenic_acid\":0.563,\n  \"phosphorus\":70.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.076,\n  \"selenium\":0.6,\n  \"vitamin_k\":30.5,\n  \"alpha_carotene\":6.0,\n  \"carbohydrate\":2.1,\n  \"sugar\":0.18,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":79.0,\n  \"fat_poly_unsaturated\":0.409,\n  \"lipid_total\":0.69,\n  \"prompt\":\"ALFALFA SEEDS,SPROUTED,RAW -- 1 cup\",\n  \"vitamin_e\":0.02,\n  \"vitamin_b6\":0.034,\n  \"fiber\":1.9,\n  \"vitamin_c\":8.2,\n  \"protein\":3.99,\n  \"vitamin_a\":155.0,\n  \"niacin\":0.481,\n  \"fat_saturated\":0.069,\n  \"weight1_gram\":33.0,\n  \"folate_dfe\":36.0,\n  \"choline\":14.4,\n  \"sodium\":6.0,\n  \"magnesium\":27.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":32.0,\n  \"ndb_no\":11001,\n  \"folate_total\":36.0,\n  \"lutein_zeaxanthin\":30.5,\n  \"iron\":0.96,\n  \"id\":2763,\n  \"energy_in_kcal\":23.0,\n  \"fat_mono_unsaturated\":0.056,\n  \"description\":\"ALFALFA SEEDS,SPROUTED,RAW\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":36.0,\n  \"manganese\":0.188,\n  \"zinc\":0.92,\n  \"riboflavin\":0.126,\n  \"copper\":0.157},\n \"843\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.0,\n  \"phosphorus\":0.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.0,\n  \"selenium\":0.0,\n  \"vitamin_k\":43.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.0,\n  \"sugar\":0.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":0.0,\n  \"fat_poly_unsaturated\":26.1,\n  \"lipid_total\":100.0,\n  \"prompt\":\n  \"SHORTENING,HOUSEHOLD,SOYBN (HYDR) -COTTONSEED (HYDR) -- 1 cup\",\n  \"vitamin_e\":0.8,\n  \"vitamin_b6\":0.0,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":0.0,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.0,\n  \"fat_saturated\":25.0,\n  \"weight1_gram\":205.0,\n  \"folate_dfe\":0.0,\n  \"choline\":0.2,\n  \"sodium\":0.0,\n  \"magnesium\":0.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":0.0,\n  \"ndb_no\":4031,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":43.0,\n  \"iron\":0.0,\n  \"id\":843,\n  \"energy_in_kcal\":884.0,\n  \"fat_mono_unsaturated\":44.5,\n  \"description\":\n  \"SHORTENING,HOUSEHOLD,SOYBN (HYDR) -COTTONSEED (HYDR)\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":-1.0,\n  \"zinc\":0.0,\n  \"riboflavin\":0.0,\n  \"copper\":0.0},\n \"300\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":265.0,\n  \"pantothenic_acid\":0.413,\n  \"phosphorus\":512.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.027,\n  \"selenium\":13.9,\n  \"vitamin_k\":2.8,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":1.28,\n  \"sugar\":0.52,\n  \"vitamin_b12\":0.83,\n  \"cholesterol\":105.0,\n  \"retinol\":258.0,\n  \"potassium\":98.0,\n  \"fat_poly_unsaturated\":0.942,\n  \"lipid_total\":33.14,\n  \"prompt\":\"CHEESE,CHEDDAR -- 1 cup, diced\",\n  \"vitamin_e\":0.29,\n  \"vitamin_b6\":0.074,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":24.9,\n  \"vitamin_a\":1002.0,\n  \"niacin\":0.08,\n  \"fat_saturated\":21.092,\n  \"weight1_gram\":132.0,\n  \"folate_dfe\":18.0,\n  \"choline\":16.5,\n  \"sodium\":621.0,\n  \"magnesium\":28.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, diced\",\n  \"calcium\":721.0,\n  \"ndb_no\":1009,\n  \"folate_total\":18.0,\n  \"lutein_zeaxanthin\":2.8,\n  \"iron\":0.68,\n  \"id\":300,\n  \"energy_in_kcal\":403.0,\n  \"fat_mono_unsaturated\":9.391,\n  \"description\":\"CHEESE,CHEDDAR\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":18.0,\n  \"manganese\":0.01,\n  \"zinc\":3.11,\n  \"riboflavin\":0.375,\n  \"copper\":0.031},\n \"3468\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":290.0,\n  \"pantothenic_acid\":0.142,\n  \"phosphorus\":30.0,\n  \"beta_crytoxanthin\":2312.0,\n  \"thiamin\":0.072,\n  \"selenium\":0.4,\n  \"vitamin_k\":102.5,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":3.29,\n  \"sugar\":1.19,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":247.0,\n  \"fat_poly_unsaturated\":0.16,\n  \"lipid_total\":0.3,\n  \"prompt\":\"LETTUCE,COS OR ROMAINE,RAW -- 1 cup, shredded\",\n  \"vitamin_e\":0.13,\n  \"vitamin_b6\":0.074,\n  \"fiber\":2.1,\n  \"vitamin_c\":24.0,\n  \"protein\":1.23,\n  \"vitamin_a\":5807.0,\n  \"niacin\":0.313,\n  \"fat_saturated\":0.039,\n  \"weight1_gram\":46.9,\n  \"folate_dfe\":136.0,\n  \"choline\":9.9,\n  \"sodium\":8.0,\n  \"magnesium\":14.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, shredded\",\n  \"calcium\":33.0,\n  \"ndb_no\":11251,\n  \"folate_total\":136.0,\n  \"lutein_zeaxanthin\":102.5,\n  \"iron\":0.97,\n  \"id\":3468,\n  \"energy_in_kcal\":17.0,\n  \"fat_mono_unsaturated\":0.012,\n  \"description\":\"LETTUCE,COS OR ROMAINE,RAW\",\n  \"refuse_percent\":6.0,\n  \"food_folate\":136.0,\n  \"manganese\":0.155,\n  \"zinc\":0.23,\n  \"riboflavin\":0.067,\n  \"copper\":0.048},\n \"1261\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.0,\n  \"phosphorus\":0.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.0,\n  \"selenium\":0.0,\n  \"vitamin_k\":122.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.0,\n  \"sugar\":0.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":0.0,\n  \"fat_poly_unsaturated\":25.588,\n  \"lipid_total\":100.0,\n  \"prompt\":\n  \"OIL,INDUSTRIAL,CANOLA W\\/ ANTIFOAMING AGENT -- 1 tablespoon\",\n  \"vitamin_e\":17.1,\n  \"vitamin_b6\":0.0,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":0.0,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.0,\n  \"fat_saturated\":7.615,\n  \"weight1_gram\":13.6,\n  \"folate_dfe\":0.0,\n  \"choline\":0.2,\n  \"sodium\":0.0,\n  \"magnesium\":0.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 tablespoon\",\n  \"calcium\":0.0,\n  \"ndb_no\":4643,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":122.0,\n  \"iron\":0.0,\n  \"id\":1261,\n  \"energy_in_kcal\":884.0,\n  \"fat_mono_unsaturated\":62.093,\n  \"description\":\"OIL,INDUSTRIAL,CANOLA W\\/ ANTIFOAMING AGENT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":0.0,\n  \"zinc\":0.0,\n  \"riboflavin\":0.0,\n  \"copper\":0.0},\n \"3469\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":25.0,\n  \"pantothenic_acid\":0.091,\n  \"phosphorus\":20.0,\n  \"beta_crytoxanthin\":277.0,\n  \"thiamin\":0.041,\n  \"selenium\":0.1,\n  \"vitamin_k\":24.1,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":2.97,\n  \"sugar\":1.97,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":141.0,\n  \"fat_poly_unsaturated\":0.074,\n  \"lipid_total\":0.14,\n  \"prompt\":\n  \"LETTUCE,ICEBERG (INCL CRISPHEAD TYPES),RAW -- 1 cup, shredded\",\n  \"vitamin_e\":0.18,\n  \"vitamin_b6\":0.042,\n  \"fiber\":1.2,\n  \"vitamin_c\":2.8,\n  \"protein\":0.9,\n  \"vitamin_a\":502.0,\n  \"niacin\":0.123,\n  \"fat_saturated\":0.018,\n  \"weight1_gram\":72.48,\n  \"folate_dfe\":29.0,\n  \"choline\":6.7,\n  \"sodium\":10.0,\n  \"magnesium\":7.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, shredded\",\n  \"calcium\":18.0,\n  \"ndb_no\":11252,\n  \"folate_total\":29.0,\n  \"lutein_zeaxanthin\":24.1,\n  \"iron\":0.41,\n  \"id\":3469,\n  \"energy_in_kcal\":14.0,\n  \"fat_mono_unsaturated\":0.006,\n  \"description\":\"LETTUCE,ICEBERG (INCL CRISPHEAD TYPES),RAW\",\n  \"refuse_percent\":5.0,\n  \"food_folate\":29.0,\n  \"manganese\":0.125,\n  \"zinc\":0.15,\n  \"riboflavin\":0.025,\n  \"copper\":0.025},\n \"3597\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":157.0,\n  \"pantothenic_acid\":0.317,\n  \"phosphorus\":26.0,\n  \"beta_crytoxanthin\":51.0,\n  \"thiamin\":0.054,\n  \"selenium\":0.1,\n  \"vitamin_k\":4.9,\n  \"alpha_carotene\":490.0,\n  \"carbohydrate\":6.03,\n  \"sugar\":4.2,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":211.0,\n  \"fat_poly_unsaturated\":0.07,\n  \"lipid_total\":0.3,\n  \"prompt\":\"PEPPERS,SWT,RED,RAW -- 1 cup, chopped\",\n  \"vitamin_e\":1.58,\n  \"vitamin_b6\":0.291,\n  \"fiber\":2.1,\n  \"vitamin_c\":127.7,\n  \"protein\":0.99,\n  \"vitamin_a\":3131.0,\n  \"niacin\":0.979,\n  \"fat_saturated\":0.027,\n  \"weight1_gram\":149.0,\n  \"folate_dfe\":46.0,\n  \"choline\":5.6,\n  \"sodium\":4.0,\n  \"magnesium\":12.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, chopped\",\n  \"calcium\":7.0,\n  \"ndb_no\":11821,\n  \"folate_total\":46.0,\n  \"lutein_zeaxanthin\":4.9,\n  \"iron\":0.43,\n  \"id\":3597,\n  \"energy_in_kcal\":31.0,\n  \"fat_mono_unsaturated\":0.003,\n  \"description\":\"PEPPERS,SWT,RED,RAW\",\n  \"refuse_percent\":18.0,\n  \"food_folate\":46.0,\n  \"manganese\":0.112,\n  \"zinc\":0.25,\n  \"riboflavin\":0.085,\n  \"copper\":0.017},\n \"4717\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.132,\n  \"phosphorus\":4.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.0,\n  \"selenium\":1.2,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":98.09,\n  \"sugar\":97.02,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":133.0,\n  \"fat_poly_unsaturated\":0.0,\n  \"lipid_total\":0.0,\n  \"prompt\":\"SUGARS,BROWN -- 1 cup,  packed\",\n  \"vitamin_e\":0.0,\n  \"vitamin_b6\":0.041,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":0.12,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.11,\n  \"fat_saturated\":0.0,\n  \"weight1_gram\":220.0,\n  \"folate_dfe\":1.0,\n  \"choline\":2.3,\n  \"sodium\":28.0,\n  \"magnesium\":9.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup,  packed\",\n  \"calcium\":83.0,\n  \"ndb_no\":19334,\n  \"folate_total\":1.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":0.71,\n  \"id\":4717,\n  \"energy_in_kcal\":380.0,\n  \"fat_mono_unsaturated\":0.0,\n  \"description\":\"SUGARS,BROWN\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":1.0,\n  \"manganese\":0.064,\n  \"zinc\":0.03,\n  \"riboflavin\":0.0,\n  \"copper\":0.047},\n \"6893\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":11.0,\n  \"pantothenic_acid\":0.424,\n  \"phosphorus\":210.0,\n  \"beta_crytoxanthin\":1355.0,\n  \"thiamin\":0.385,\n  \"selenium\":15.5,\n  \"vitamin_k\":0.3,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":74.26,\n  \"sugar\":0.64,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":287.0,\n  \"fat_poly_unsaturated\":2.163,\n  \"lipid_total\":4.74,\n  \"prompt\":\"CORN,YELLOW -- 1 cup\",\n  \"vitamin_e\":0.49,\n  \"vitamin_b6\":0.622,\n  \"fiber\":7.3,\n  \"vitamin_c\":0.0,\n  \"protein\":9.42,\n  \"vitamin_a\":214.0,\n  \"niacin\":3.627,\n  \"fat_saturated\":0.667,\n  \"weight1_gram\":166.0,\n  \"folate_dfe\":19.0,\n  \"choline\":-1.0,\n  \"sodium\":35.0,\n  \"magnesium\":127.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":7.0,\n  \"ndb_no\":20014,\n  \"folate_total\":19.0,\n  \"lutein_zeaxanthin\":0.3,\n  \"iron\":2.71,\n  \"id\":6893,\n  \"energy_in_kcal\":365.0,\n  \"fat_mono_unsaturated\":1.251,\n  \"description\":\"CORN,YELLOW\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":19.0,\n  \"manganese\":0.485,\n  \"zinc\":2.21,\n  \"riboflavin\":0.201,\n  \"copper\":0.314},\n \"366\":\n {\"beta_carotene\":4431.0,\n  \"lycopene\":4431.0,\n  \"vitamin_a_rae\":12.0,\n  \"pantothenic_acid\":0.037,\n  \"phosphorus\":17.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.01,\n  \"selenium\":1.3,\n  \"vitamin_k\":1.8,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":36.25,\n  \"sugar\":26.06,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":208.0,\n  \"fat_poly_unsaturated\":0.17,\n  \"lipid_total\":0.29,\n  \"prompt\":\"SAUCE,BARBECUE -- 1 serving, 2 tbsp\",\n  \"vitamin_e\":0.66,\n  \"vitamin_b6\":0.041,\n  \"fiber\":0.6,\n  \"vitamin_c\":0.7,\n  \"protein\":0.0,\n  \"vitamin_a\":235.0,\n  \"niacin\":0.487,\n  \"fat_saturated\":0.0,\n  \"weight1_gram\":34.51,\n  \"folate_dfe\":2.0,\n  \"choline\":2.9,\n  \"sodium\":1119.0,\n  \"magnesium\":12.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 serving, 2 tbsp\",\n  \"calcium\":12.0,\n  \"ndb_no\":6150,\n  \"folate_total\":2.0,\n  \"lutein_zeaxanthin\":1.8,\n  \"iron\":0.21,\n  \"id\":366,\n  \"energy_in_kcal\":150.0,\n  \"fat_mono_unsaturated\":0.074,\n  \"description\":\"SAUCE,BARBECUE\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":2.0,\n  \"manganese\":0.225,\n  \"zinc\":0.13,\n  \"riboflavin\":0.032,\n  \"copper\":0.079},\n \"3662\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":38.0,\n  \"pantothenic_acid\":0.104,\n  \"phosphorus\":108.0,\n  \"beta_crytoxanthin\":2477.0,\n  \"thiamin\":0.266,\n  \"selenium\":1.8,\n  \"vitamin_k\":24.8,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":14.45,\n  \"sugar\":5.67,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":244.0,\n  \"fat_poly_unsaturated\":0.187,\n  \"lipid_total\":0.4,\n  \"prompt\":\"PEAS,GREEN,RAW -- 1 cup\",\n  \"vitamin_e\":0.13,\n  \"vitamin_b6\":0.169,\n  \"fiber\":5.1,\n  \"vitamin_c\":40.0,\n  \"protein\":5.42,\n  \"vitamin_a\":765.0,\n  \"niacin\":2.09,\n  \"fat_saturated\":0.071,\n  \"weight1_gram\":145.0,\n  \"folate_dfe\":65.0,\n  \"choline\":28.4,\n  \"sodium\":5.0,\n  \"magnesium\":33.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":25.0,\n  \"ndb_no\":11304,\n  \"folate_total\":65.0,\n  \"lutein_zeaxanthin\":24.8,\n  \"iron\":1.47,\n  \"id\":3662,\n  \"energy_in_kcal\":81.0,\n  \"fat_mono_unsaturated\":0.035,\n  \"description\":\"PEAS,GREEN,RAW\",\n  \"refuse_percent\":62.0,\n  \"food_folate\":65.0,\n  \"manganese\":0.41,\n  \"zinc\":1.24,\n  \"riboflavin\":0.132,\n  \"copper\":0.176},\n \"4718\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.0,\n  \"phosphorus\":0.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.0,\n  \"selenium\":0.6,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":99.98,\n  \"sugar\":99.91,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":2.0,\n  \"fat_poly_unsaturated\":0.0,\n  \"lipid_total\":0.0,\n  \"prompt\":\"SUGARS,GRANULATED -- 1 serving, packet\",\n  \"vitamin_e\":0.0,\n  \"vitamin_b6\":0.0,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":0.0,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.0,\n  \"fat_saturated\":0.0,\n  \"weight1_gram\":2.84,\n  \"folate_dfe\":0.0,\n  \"choline\":0.0,\n  \"sodium\":0.0,\n  \"magnesium\":0.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 serving, packet\",\n  \"calcium\":1.0,\n  \"ndb_no\":19335,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":0.01,\n  \"id\":4718,\n  \"energy_in_kcal\":387.0,\n  \"fat_mono_unsaturated\":0.0,\n  \"description\":\"SUGARS,GRANULATED\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":0.0,\n  \"zinc\":0.0,\n  \"riboflavin\":0.019,\n  \"copper\":0.0},\n \"3183\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.88,\n  \"phosphorus\":531.0,\n  \"beta_crytoxanthin\":23.0,\n  \"thiamin\":0.363,\n  \"selenium\":20.3,\n  \"vitamin_k\":34.7,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":30.16,\n  \"sugar\":5.01,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":632.0,\n  \"fat_poly_unsaturated\":8.546,\n  \"lipid_total\":47.77,\n  \"prompt\":\"CASHEW NUTS,OIL RSTD,W\\/SALT -- 1 cup, whole\",\n  \"vitamin_e\":0.92,\n  \"vitamin_b6\":0.323,\n  \"fiber\":3.3,\n  \"vitamin_c\":0.3,\n  \"protein\":16.84,\n  \"vitamin_a\":0.0,\n  \"niacin\":1.736,\n  \"fat_saturated\":8.478,\n  \"weight1_gram\":129.08,\n  \"folate_dfe\":25.0,\n  \"choline\":61.0,\n  \"sodium\":308.0,\n  \"magnesium\":273.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, whole\",\n  \"calcium\":43.0,\n  \"ndb_no\":12586,\n  \"folate_total\":25.0,\n  \"lutein_zeaxanthin\":34.7,\n  \"iron\":6.05,\n  \"id\":3183,\n  \"energy_in_kcal\":581.0,\n  \"fat_mono_unsaturated\":25.923,\n  \"description\":\"CASHEW NUTS,OIL RSTD,W\\/SALT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":25.0,\n  \"manganese\":1.668,\n  \"zinc\":5.35,\n  \"riboflavin\":0.218,\n  \"copper\":2.043},\n \"2319\":\n {\"beta_carotene\":-1.0,\n  \"lycopene\":-1.0,\n  \"vitamin_a_rae\":2.0,\n  \"pantothenic_acid\":0.232,\n  \"phosphorus\":15.0,\n  \"beta_crytoxanthin\":-1.0,\n  \"thiamin\":0.05,\n  \"selenium\":-1.0,\n  \"vitamin_k\":-1.0,\n  \"alpha_carotene\":-1.0,\n  \"carbohydrate\":10.7,\n  \"sugar\":-1.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":145.0,\n  \"fat_poly_unsaturated\":0.089,\n  \"lipid_total\":0.3,\n  \"prompt\":\"LEMONS,RAW,WITH PEEL -- 1 fruit, without seeds\",\n  \"vitamin_e\":-1.0,\n  \"vitamin_b6\":0.109,\n  \"fiber\":4.7,\n  \"vitamin_c\":77.0,\n  \"protein\":1.2,\n  \"vitamin_a\":30.0,\n  \"niacin\":0.2,\n  \"fat_saturated\":0.039,\n  \"weight1_gram\":108.0,\n  \"folate_dfe\":-1.0,\n  \"choline\":-1.0,\n  \"sodium\":3.0,\n  \"magnesium\":12.0,\n  \"folic_acid\":-1.0,\n  \"weight1_description\":\"1 fruit, without seeds\",\n  \"calcium\":61.0,\n  \"ndb_no\":9151,\n  \"folate_total\":-1.0,\n  \"lutein_zeaxanthin\":-1.0,\n  \"iron\":0.7,\n  \"id\":2319,\n  \"energy_in_kcal\":20.0,\n  \"fat_mono_unsaturated\":0.011,\n  \"description\":\"LEMONS,RAW,WITH PEEL\",\n  \"refuse_percent\":2.0,\n  \"food_folate\":-1.0,\n  \"manganese\":-1.0,\n  \"zinc\":0.1,\n  \"riboflavin\":0.04,\n  \"copper\":0.26},\n \"4719\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.0,\n  \"phosphorus\":0.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.0,\n  \"selenium\":0.6,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":99.6,\n  \"sugar\":97.91,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":2.0,\n  \"fat_poly_unsaturated\":0.05,\n  \"lipid_total\":0.1,\n  \"prompt\":\"SUGARS,POWDERED -- 1 cup, unsifted\",\n  \"vitamin_e\":0.0,\n  \"vitamin_b6\":0.0,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":0.0,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.0,\n  \"fat_saturated\":0.02,\n  \"weight1_gram\":120.0,\n  \"folate_dfe\":0.0,\n  \"choline\":0.0,\n  \"sodium\":1.0,\n  \"magnesium\":0.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, unsifted\",\n  \"calcium\":1.0,\n  \"ndb_no\":19336,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":0.02,\n  \"id\":4719,\n  \"energy_in_kcal\":389.0,\n  \"fat_mono_unsaturated\":0.032,\n  \"description\":\"SUGARS,POWDERED\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":0.001,\n  \"zinc\":0.0,\n  \"riboflavin\":0.019,\n  \"copper\":0.001},\n \"3791\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":961.0,\n  \"pantothenic_acid\":0.884,\n  \"phosphorus\":54.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.107,\n  \"selenium\":0.2,\n  \"vitamin_k\":2.3,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":20.71,\n  \"sugar\":11.09,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":475.0,\n  \"fat_poly_unsaturated\":0.092,\n  \"lipid_total\":0.15,\n  \"prompt\":\n  \"SWEET POTATO,CKD,BKD IN SKN,W\\/ SALT -- 1 medium,  (2\\\" dia, 5\\\" long, raw)\",\n  \"vitamin_e\":0.71,\n  \"vitamin_b6\":0.286,\n  \"fiber\":3.3,\n  \"vitamin_c\":19.6,\n  \"protein\":2.01,\n  \"vitamin_a\":19218.0,\n  \"niacin\":1.487,\n  \"fat_saturated\":0.052,\n  \"weight1_gram\":114.0,\n  \"folate_dfe\":6.0,\n  \"choline\":13.1,\n  \"sodium\":246.0,\n  \"magnesium\":27.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 medium,  (2\\\" dia, 5\\\" long, raw)\",\n  \"calcium\":38.0,\n  \"ndb_no\":11875,\n  \"folate_total\":6.0,\n  \"lutein_zeaxanthin\":2.3,\n  \"iron\":0.69,\n  \"id\":3791,\n  \"energy_in_kcal\":92.0,\n  \"fat_mono_unsaturated\":0.002,\n  \"description\":\"SWEET POTATO,CKD,BKD IN SKN,W\\/ SALT\",\n  \"refuse_percent\":22.0,\n  \"food_folate\":6.0,\n  \"manganese\":0.497,\n  \"zinc\":0.32,\n  \"riboflavin\":0.106,\n  \"copper\":0.161},\n \"2799\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":1.0,\n  \"pantothenic_acid\":0.125,\n  \"phosphorus\":24.0,\n  \"beta_crytoxanthin\":26.0,\n  \"thiamin\":0.024,\n  \"selenium\":0.4,\n  \"vitamin_k\":2.2,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":7.68,\n  \"sugar\":4.89,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":153.0,\n  \"fat_poly_unsaturated\":0.155,\n  \"lipid_total\":0.3,\n  \"prompt\":\"STRAWBERRIES,RAW -- 1 cup,  halves\",\n  \"vitamin_e\":0.29,\n  \"vitamin_b6\":0.047,\n  \"fiber\":2.0,\n  \"vitamin_c\":58.8,\n  \"protein\":0.67,\n  \"vitamin_a\":12.0,\n  \"niacin\":0.386,\n  \"fat_saturated\":0.015,\n  \"weight1_gram\":152.0,\n  \"folate_dfe\":24.0,\n  \"choline\":5.7,\n  \"sodium\":1.0,\n  \"magnesium\":13.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup,  halves\",\n  \"calcium\":16.0,\n  \"ndb_no\":9316,\n  \"folate_total\":24.0,\n  \"lutein_zeaxanthin\":2.2,\n  \"iron\":0.41,\n  \"id\":2799,\n  \"energy_in_kcal\":32.0,\n  \"fat_mono_unsaturated\":0.043,\n  \"description\":\"STRAWBERRIES,RAW\",\n  \"refuse_percent\":6.0,\n  \"food_folate\":24.0,\n  \"manganese\":0.386,\n  \"zinc\":0.14,\n  \"riboflavin\":0.022,\n  \"copper\":0.048},\n \"943\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":819.0,\n  \"pantothenic_acid\":0.084,\n  \"phosphorus\":23.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.01,\n  \"selenium\":0.0,\n  \"vitamin_k\":93.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.7,\n  \"sugar\":0.0,\n  \"vitamin_b12\":0.1,\n  \"cholesterol\":0.0,\n  \"retinol\":768.0,\n  \"potassium\":42.0,\n  \"fat_poly_unsaturated\":24.302,\n  \"lipid_total\":80.71,\n  \"prompt\":\"MARGARINE,REG,UNSPEC OILS,W\\/SALT -- 1 tsp\",\n  \"vitamin_e\":9.0,\n  \"vitamin_b6\":0.009,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.2,\n  \"protein\":0.16,\n  \"vitamin_a\":3571.0,\n  \"niacin\":0.023,\n  \"fat_saturated\":15.189,\n  \"weight1_gram\":4.7,\n  \"folate_dfe\":1.0,\n  \"choline\":12.4,\n  \"sodium\":943.0,\n  \"magnesium\":3.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 tsp\",\n  \"calcium\":30.0,\n  \"ndb_no\":4132,\n  \"folate_total\":1.0,\n  \"lutein_zeaxanthin\":93.0,\n  \"iron\":0.06,\n  \"id\":943,\n  \"energy_in_kcal\":717.0,\n  \"fat_mono_unsaturated\":38.877,\n  \"description\":\"MARGARINE,REG,UNSPEC OILS,W\\/SALT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":1.0,\n  \"manganese\":-1.0,\n  \"zinc\":0.0,\n  \"riboflavin\":0.037,\n  \"copper\":0.0},\n \"6095\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.0,\n  \"phosphorus\":5.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.0,\n  \"selenium\":0.2,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":61.5,\n  \"sugar\":0.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":16500.0,\n  \"fat_poly_unsaturated\":0.0,\n  \"lipid_total\":0.0,\n  \"prompt\":\"LEAVENING AGENTS,CRM OF TARTAR -- 1 tsp\",\n  \"vitamin_e\":0.0,\n  \"vitamin_b6\":0.0,\n  \"fiber\":0.2,\n  \"vitamin_c\":0.0,\n  \"protein\":0.0,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.0,\n  \"fat_saturated\":0.0,\n  \"weight1_gram\":3.0,\n  \"folate_dfe\":0.0,\n  \"choline\":0.0,\n  \"sodium\":52.0,\n  \"magnesium\":2.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 tsp\",\n  \"calcium\":8.0,\n  \"ndb_no\":18373,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":3.72,\n  \"id\":6095,\n  \"energy_in_kcal\":258.0,\n  \"fat_mono_unsaturated\":0.0,\n  \"description\":\"LEAVENING AGENTS,CRM OF TARTAR\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":0.205,\n  \"zinc\":0.42,\n  \"riboflavin\":0.0,\n  \"copper\":0.195},\n \"2320\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":1.0,\n  \"pantothenic_acid\":0.103,\n  \"phosphorus\":6.0,\n  \"beta_crytoxanthin\":9.0,\n  \"thiamin\":0.03,\n  \"selenium\":0.1,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":17.0,\n  \"carbohydrate\":8.63,\n  \"sugar\":2.4,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":124.0,\n  \"fat_poly_unsaturated\":0.0,\n  \"lipid_total\":0.0,\n  \"prompt\":\"LEMON JUICE,RAW -- 1 cup\",\n  \"vitamin_e\":0.15,\n  \"vitamin_b6\":0.051,\n  \"fiber\":0.4,\n  \"vitamin_c\":46.0,\n  \"protein\":0.38,\n  \"vitamin_a\":20.0,\n  \"niacin\":0.1,\n  \"fat_saturated\":0.0,\n  \"weight1_gram\":244.0,\n  \"folate_dfe\":13.0,\n  \"choline\":5.1,\n  \"sodium\":1.0,\n  \"magnesium\":6.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":7.0,\n  \"ndb_no\":9152,\n  \"folate_total\":13.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":0.03,\n  \"id\":2320,\n  \"energy_in_kcal\":25.0,\n  \"fat_mono_unsaturated\":0.0,\n  \"description\":\"LEMON JUICE,RAW\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":13.0,\n  \"manganese\":0.008,\n  \"zinc\":0.05,\n  \"riboflavin\":0.01,\n  \"copper\":0.029},\n \"1424\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":16.0,\n  \"pantothenic_acid\":1.058,\n  \"phosphorus\":173.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.073,\n  \"selenium\":15.7,\n  \"vitamin_k\":1.8,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.0,\n  \"sugar\":0.0,\n  \"vitamin_b12\":0.37,\n  \"cholesterol\":70.0,\n  \"retinol\":16.0,\n  \"potassium\":229.0,\n  \"fat_poly_unsaturated\":0.75,\n  \"lipid_total\":3.08,\n  \"prompt\":\n  \"CHICKEN,BROILERS OR FRYERS,MEAT ONLY,RAW -- 1 unit,  (yield from 1 lb ready-to-cook chicken)\",\n  \"vitamin_e\":0.21,\n  \"vitamin_b6\":0.43,\n  \"fiber\":0.0,\n  \"vitamin_c\":2.3,\n  \"protein\":21.39,\n  \"vitamin_a\":52.0,\n  \"niacin\":8.239,\n  \"fat_saturated\":0.79,\n  \"weight1_gram\":197.0,\n  \"folate_dfe\":7.0,\n  \"choline\":65.7,\n  \"sodium\":77.0,\n  \"magnesium\":25.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\n  \"1 unit,  (yield from 1 lb ready-to-cook chicken)\",\n  \"calcium\":12.0,\n  \"ndb_no\":5011,\n  \"folate_total\":7.0,\n  \"lutein_zeaxanthin\":1.8,\n  \"iron\":0.89,\n  \"id\":1424,\n  \"energy_in_kcal\":119.0,\n  \"fat_mono_unsaturated\":0.9,\n  \"description\":\"CHICKEN,BROILERS OR FRYERS,MEAT ONLY,RAW\",\n  \"refuse_percent\":52.0,\n  \"food_folate\":7.0,\n  \"manganese\":0.019,\n  \"zinc\":1.54,\n  \"riboflavin\":0.142,\n  \"copper\":0.053},\n \"7152\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.419,\n  \"phosphorus\":89.0,\n  \"beta_crytoxanthin\":81.0,\n  \"thiamin\":0.108,\n  \"selenium\":25.9,\n  \"vitamin_k\":0.7,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":26.54,\n  \"sugar\":0.8,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":44.0,\n  \"fat_poly_unsaturated\":0.213,\n  \"lipid_total\":0.54,\n  \"prompt\":\"SPAGHETTI,WHOLE-WHEAT,CKD -- 1 cup\",\n  \"vitamin_e\":0.3,\n  \"vitamin_b6\":0.079,\n  \"fiber\":4.5,\n  \"vitamin_c\":0.0,\n  \"protein\":5.33,\n  \"vitamin_a\":3.0,\n  \"niacin\":0.707,\n  \"fat_saturated\":0.099,\n  \"weight1_gram\":140.0,\n  \"folate_dfe\":5.0,\n  \"choline\":10.7,\n  \"sodium\":3.0,\n  \"magnesium\":30.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":15.0,\n  \"ndb_no\":20125,\n  \"folate_total\":5.0,\n  \"lutein_zeaxanthin\":0.7,\n  \"iron\":1.06,\n  \"id\":7152,\n  \"energy_in_kcal\":124.0,\n  \"fat_mono_unsaturated\":0.075,\n  \"description\":\"SPAGHETTI,WHOLE-WHEAT,CKD\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":5.0,\n  \"manganese\":1.379,\n  \"zinc\":0.81,\n  \"riboflavin\":0.045,\n  \"copper\":0.167},\n \"241\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":18.0,\n  \"pantothenic_acid\":1.178,\n  \"phosphorus\":167.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.079,\n  \"selenium\":13.5,\n  \"vitamin_k\":2.9,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.0,\n  \"sugar\":0.0,\n  \"vitamin_b12\":0.36,\n  \"cholesterol\":80.0,\n  \"retinol\":18.0,\n  \"potassium\":229.0,\n  \"fat_poly_unsaturated\":0.95,\n  \"lipid_total\":3.81,\n  \"prompt\":\n  \"CHICKEN,BROILERS OR FRYERS,LEG,MEAT ONLY,RAW -- 1 leg, bone and skin removed (yield from 1 lb ready-to-cook chicken)\",\n  \"vitamin_e\":0.32,\n  \"vitamin_b6\":0.33,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":20.13,\n  \"vitamin_a\":61.0,\n  \"niacin\":6.067,\n  \"fat_saturated\":0.98,\n  \"weight1_gram\":78.0,\n  \"folate_dfe\":10.0,\n  \"choline\":61.8,\n  \"sodium\":86.0,\n  \"magnesium\":23.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\n  \"1 leg, bone and skin removed (yield from 1 lb ready-to-cook chicken)\",\n  \"calcium\":11.0,\n  \"ndb_no\":5080,\n  \"folate_total\":10.0,\n  \"lutein_zeaxanthin\":2.9,\n  \"iron\":1.02,\n  \"id\":241,\n  \"energy_in_kcal\":120.0,\n  \"fat_mono_unsaturated\":1.18,\n  \"description\":\"CHICKEN,BROILERS OR FRYERS,LEG,MEAT ONLY,RAW\",\n  \"refuse_percent\":43.0,\n  \"food_folate\":10.0,\n  \"manganese\":0.021,\n  \"zinc\":2.06,\n  \"riboflavin\":0.193,\n  \"copper\":0.064},\n \"2417\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":17.0,\n  \"pantothenic_acid\":0.015,\n  \"phosphorus\":3.0,\n  \"beta_crytoxanthin\":510.0,\n  \"thiamin\":0.003,\n  \"selenium\":0.9,\n  \"vitamin_k\":1.4,\n  \"alpha_carotene\":8.0,\n  \"carbohydrate\":5.61,\n  \"sugar\":0.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":9.0,\n  \"fat_poly_unsaturated\":0.586,\n  \"lipid_total\":6.87,\n  \"prompt\":\"OLIVES,RIPE,CND (JUMBO-SUPER COLOSSAL) -- 1 jumbo\",\n  \"vitamin_e\":1.65,\n  \"vitamin_b6\":0.012,\n  \"fiber\":2.5,\n  \"vitamin_c\":1.5,\n  \"protein\":0.97,\n  \"vitamin_a\":346.0,\n  \"niacin\":0.022,\n  \"fat_saturated\":0.909,\n  \"weight1_gram\":8.3,\n  \"folate_dfe\":0.0,\n  \"choline\":6.6,\n  \"sodium\":898.0,\n  \"magnesium\":4.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 jumbo\",\n  \"calcium\":94.0,\n  \"ndb_no\":9194,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":1.4,\n  \"iron\":3.32,\n  \"id\":2417,\n  \"energy_in_kcal\":81.0,\n  \"fat_mono_unsaturated\":5.071,\n  \"description\":\"OLIVES,RIPE,CND (JUMBO-SUPER COLOSSAL)\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":0.02,\n  \"zinc\":0.22,\n  \"riboflavin\":0.0,\n  \"copper\":0.226},\n \"401\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":220.0,\n  \"pantothenic_acid\":0.429,\n  \"phosphorus\":567.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.063,\n  \"selenium\":18.2,\n  \"vitamin_k\":2.5,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":5.38,\n  \"sugar\":1.32,\n  \"vitamin_b12\":3.34,\n  \"cholesterol\":92.0,\n  \"retinol\":214.0,\n  \"potassium\":77.0,\n  \"fat_poly_unsaturated\":0.972,\n  \"lipid_total\":27.8,\n  \"prompt\":\"CHEESE,SWISS -- 1 cup, diced\",\n  \"vitamin_e\":0.38,\n  \"vitamin_b6\":0.083,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":26.93,\n  \"vitamin_a\":830.0,\n  \"niacin\":0.092,\n  \"fat_saturated\":17.779,\n  \"weight1_gram\":132.0,\n  \"folate_dfe\":6.0,\n  \"choline\":15.5,\n  \"sodium\":192.0,\n  \"magnesium\":38.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, diced\",\n  \"calcium\":791.0,\n  \"ndb_no\":1040,\n  \"folate_total\":6.0,\n  \"lutein_zeaxanthin\":2.5,\n  \"iron\":0.2,\n  \"id\":401,\n  \"energy_in_kcal\":380.0,\n  \"fat_mono_unsaturated\":7.274,\n  \"description\":\"CHEESE,SWISS\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":6.0,\n  \"manganese\":0.005,\n  \"zinc\":4.36,\n  \"riboflavin\":0.296,\n  \"copper\":0.043},\n \"3473\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":375.0,\n  \"pantothenic_acid\":0.144,\n  \"phosphorus\":28.0,\n  \"beta_crytoxanthin\":1724.0,\n  \"thiamin\":0.064,\n  \"selenium\":1.5,\n  \"vitamin_k\":140.3,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":2.26,\n  \"sugar\":0.48,\n  \"vitamin_b12\":-1.0,\n  \"cholesterol\":-1.0,\n  \"retinol\":-1.0,\n  \"potassium\":187.0,\n  \"fat_poly_unsaturated\":-1.0,\n  \"lipid_total\":0.22,\n  \"prompt\":\"LETTUCE,RED LEAF,RAW -- 1 cup, shredded\",\n  \"vitamin_e\":0.15,\n  \"vitamin_b6\":0.1,\n  \"fiber\":0.9,\n  \"vitamin_c\":3.7,\n  \"protein\":1.33,\n  \"vitamin_a\":7492.0,\n  \"niacin\":0.321,\n  \"fat_saturated\":-1.0,\n  \"weight1_gram\":28.27,\n  \"folate_dfe\":-1.0,\n  \"choline\":11.8,\n  \"sodium\":25.0,\n  \"magnesium\":12.0,\n  \"folic_acid\":-1.0,\n  \"weight1_description\":\"1 cup, shredded\",\n  \"calcium\":33.0,\n  \"ndb_no\":11257,\n  \"folate_total\":36.0,\n  \"lutein_zeaxanthin\":140.3,\n  \"iron\":1.2,\n  \"id\":3473,\n  \"energy_in_kcal\":16.0,\n  \"fat_mono_unsaturated\":-1.0,\n  \"description\":\"LETTUCE,RED LEAF,RAW\",\n  \"refuse_percent\":20.0,\n  \"food_folate\":36.0,\n  \"manganese\":0.203,\n  \"zinc\":0.2,\n  \"riboflavin\":0.077,\n  \"copper\":0.028},\n \"1489\":\n {\"beta_carotene\":-1.0,\n  \"lycopene\":-1.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.825,\n  \"phosphorus\":100.0,\n  \"beta_crytoxanthin\":-1.0,\n  \"thiamin\":0.39,\n  \"selenium\":0.6,\n  \"vitamin_k\":-1.0,\n  \"alpha_carotene\":-1.0,\n  \"carbohydrate\":13.05,\n  \"sugar\":-1.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":307.0,\n  \"fat_poly_unsaturated\":0.407,\n  \"lipid_total\":0.7,\n  \"prompt\":\"BEANS,NAVY,MATURE SEEDS,SPROUTED,RAW -- 1 cup\",\n  \"vitamin_e\":-1.0,\n  \"vitamin_b6\":0.191,\n  \"fiber\":-1.0,\n  \"vitamin_c\":18.8,\n  \"protein\":6.15,\n  \"vitamin_a\":4.0,\n  \"niacin\":1.22,\n  \"fat_saturated\":0.085,\n  \"weight1_gram\":104.0,\n  \"folate_dfe\":132.0,\n  \"choline\":-1.0,\n  \"sodium\":13.0,\n  \"magnesium\":101.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":15.0,\n  \"ndb_no\":11046,\n  \"folate_total\":132.0,\n  \"lutein_zeaxanthin\":-1.0,\n  \"iron\":1.93,\n  \"id\":1489,\n  \"energy_in_kcal\":67.0,\n  \"fat_mono_unsaturated\":0.052,\n  \"description\":\"BEANS,NAVY,MATURE SEEDS,SPROUTED,RAW\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":132.0,\n  \"manganese\":0.408,\n  \"zinc\":0.89,\n  \"riboflavin\":0.215,\n  \"copper\":0.356},\n \"1649\":\n {\"beta_carotene\":1.0,\n  \"lycopene\":1.0,\n  \"vitamin_a_rae\":835.0,\n  \"pantothenic_acid\":0.273,\n  \"phosphorus\":35.0,\n  \"beta_crytoxanthin\":256.0,\n  \"thiamin\":0.066,\n  \"selenium\":0.1,\n  \"vitamin_k\":13.2,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":9.58,\n  \"sugar\":4.74,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":320.0,\n  \"fat_poly_unsaturated\":0.117,\n  \"lipid_total\":0.24,\n  \"prompt\":\"CARROTS,RAW -- 1 cup, chopped\",\n  \"vitamin_e\":0.66,\n  \"vitamin_b6\":0.138,\n  \"fiber\":2.8,\n  \"vitamin_c\":5.9,\n  \"protein\":0.93,\n  \"vitamin_a\":16706.0,\n  \"niacin\":0.983,\n  \"fat_saturated\":0.037,\n  \"weight1_gram\":128.0,\n  \"folate_dfe\":19.0,\n  \"choline\":8.8,\n  \"sodium\":69.0,\n  \"magnesium\":12.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, chopped\",\n  \"calcium\":33.0,\n  \"ndb_no\":11124,\n  \"folate_total\":19.0,\n  \"lutein_zeaxanthin\":13.2,\n  \"iron\":0.3,\n  \"id\":1649,\n  \"energy_in_kcal\":41.0,\n  \"fat_mono_unsaturated\":0.014,\n  \"description\":\"CARROTS,RAW\",\n  \"refuse_percent\":11.0,\n  \"food_folate\":19.0,\n  \"manganese\":0.143,\n  \"zinc\":0.24,\n  \"riboflavin\":0.058,\n  \"copper\":0.045},\n \"6097\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":11.3,\n  \"phosphorus\":1290.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":2.36,\n  \"selenium\":24.1,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":38.2,\n  \"sugar\":0.0,\n  \"vitamin_b12\":0.02,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":2000.0,\n  \"fat_poly_unsaturated\":0.01,\n  \"lipid_total\":4.6,\n  \"prompt\":\"LEAVENING AGENTS,YEAST,BAKER'S,ACTIVE DRY -- 1 tbsp\",\n  \"vitamin_e\":0.0,\n  \"vitamin_b6\":1.55,\n  \"fiber\":21.0,\n  \"vitamin_c\":0.3,\n  \"protein\":38.3,\n  \"vitamin_a\":1.0,\n  \"niacin\":39.75,\n  \"fat_saturated\":0.595,\n  \"weight1_gram\":12.0,\n  \"folate_dfe\":2340.0,\n  \"choline\":32.0,\n  \"sodium\":50.0,\n  \"magnesium\":98.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 tbsp\",\n  \"calcium\":64.0,\n  \"ndb_no\":18375,\n  \"folate_total\":2340.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":16.6,\n  \"id\":6097,\n  \"energy_in_kcal\":295.0,\n  \"fat_mono_unsaturated\":2.562,\n  \"description\":\"LEAVENING AGENTS,YEAST,BAKER'S,ACTIVE DRY\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":2340.0,\n  \"manganese\":0.55,\n  \"zinc\":6.4,\n  \"riboflavin\":5.47,\n  \"copper\":0.5},\n \"594\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":61.0,\n  \"pantothenic_acid\":0.357,\n  \"phosphorus\":101.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.045,\n  \"selenium\":3.1,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":4.96,\n  \"sugar\":5.09,\n  \"vitamin_b12\":0.53,\n  \"cholesterol\":2.0,\n  \"retinol\":61.0,\n  \"potassium\":156.0,\n  \"fat_poly_unsaturated\":0.003,\n  \"lipid_total\":0.08,\n  \"prompt\":\"MILK,NONFAT,FLUID,W\\/ VIT A (FAT FREE OR SKIM) -- 1 cup\",\n  \"vitamin_e\":0.01,\n  \"vitamin_b6\":0.037,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":3.37,\n  \"vitamin_a\":204.0,\n  \"niacin\":0.094,\n  \"fat_saturated\":0.051,\n  \"weight1_gram\":245.0,\n  \"folate_dfe\":5.0,\n  \"choline\":15.6,\n  \"sodium\":42.0,\n  \"magnesium\":11.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":125.0,\n  \"ndb_no\":1085,\n  \"folate_total\":5.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":0.03,\n  \"id\":594,\n  \"energy_in_kcal\":34.0,\n  \"fat_mono_unsaturated\":0.021,\n  \"description\":\"MILK,NONFAT,FLUID,W\\/ VIT A (FAT FREE OR SKIM)\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":5.0,\n  \"manganese\":0.003,\n  \"zinc\":0.42,\n  \"riboflavin\":0.182,\n  \"copper\":0.013},\n \"2738\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.095,\n  \"phosphorus\":101.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.106,\n  \"selenium\":0.6,\n  \"vitamin_k\":3.5,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":79.18,\n  \"sugar\":59.19,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":749.0,\n  \"fat_poly_unsaturated\":0.037,\n  \"lipid_total\":0.46,\n  \"prompt\":\"RAISINS,SEEDLESS -- 1 cup,  packed\",\n  \"vitamin_e\":0.12,\n  \"vitamin_b6\":0.174,\n  \"fiber\":3.7,\n  \"vitamin_c\":2.3,\n  \"protein\":3.07,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.766,\n  \"fat_saturated\":0.058,\n  \"weight1_gram\":165.0,\n  \"folate_dfe\":5.0,\n  \"choline\":11.1,\n  \"sodium\":11.0,\n  \"magnesium\":32.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup,  packed\",\n  \"calcium\":50.0,\n  \"ndb_no\":9298,\n  \"folate_total\":5.0,\n  \"lutein_zeaxanthin\":3.5,\n  \"iron\":1.88,\n  \"id\":2738,\n  \"energy_in_kcal\":299.0,\n  \"fat_mono_unsaturated\":0.051,\n  \"description\":\"RAISINS,SEEDLESS\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":5.0,\n  \"manganese\":0.299,\n  \"zinc\":0.22,\n  \"riboflavin\":0.125,\n  \"copper\":0.318},\n \"6898\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":11.0,\n  \"pantothenic_acid\":0.425,\n  \"phosphorus\":241.0,\n  \"beta_crytoxanthin\":1355.0,\n  \"thiamin\":0.385,\n  \"selenium\":15.5,\n  \"vitamin_k\":0.3,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":76.89,\n  \"sugar\":0.64,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":287.0,\n  \"fat_poly_unsaturated\":1.638,\n  \"lipid_total\":3.59,\n  \"prompt\":\"CORNMEAL,WHOLE-GRAIN,YEL -- 1 cup\",\n  \"vitamin_e\":0.42,\n  \"vitamin_b6\":0.304,\n  \"fiber\":7.3,\n  \"vitamin_c\":0.0,\n  \"protein\":8.12,\n  \"vitamin_a\":214.0,\n  \"niacin\":3.632,\n  \"fat_saturated\":0.505,\n  \"weight1_gram\":122.0,\n  \"folate_dfe\":25.0,\n  \"choline\":21.6,\n  \"sodium\":35.0,\n  \"magnesium\":127.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":6.0,\n  \"ndb_no\":20020,\n  \"folate_total\":25.0,\n  \"lutein_zeaxanthin\":0.3,\n  \"iron\":3.45,\n  \"id\":6898,\n  \"energy_in_kcal\":362.0,\n  \"fat_mono_unsaturated\":0.948,\n  \"description\":\"CORNMEAL,WHOLE-GRAIN,YEL\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":25.0,\n  \"manganese\":0.498,\n  \"zinc\":1.82,\n  \"riboflavin\":0.201,\n  \"copper\":0.193},\n \"1874\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.063,\n  \"phosphorus\":7.0,\n  \"beta_crytoxanthin\":16.0,\n  \"thiamin\":0.003,\n  \"selenium\":0.1,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":11.54,\n  \"sugar\":10.93,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":126.0,\n  \"fat_poly_unsaturated\":0.031,\n  \"lipid_total\":0.1,\n  \"prompt\":\n  \"APPLE JUC,FRZ CONC,UNSWTND,DIL W\\/3 VOLUME H2O WO\\/ VIT C -- 1 cup\",\n  \"vitamin_e\":0.01,\n  \"vitamin_b6\":0.033,\n  \"fiber\":0.1,\n  \"vitamin_c\":0.6,\n  \"protein\":0.14,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.038,\n  \"fat_saturated\":0.018,\n  \"weight1_gram\":239.0,\n  \"folate_dfe\":0.0,\n  \"choline\":1.8,\n  \"sodium\":7.0,\n  \"magnesium\":5.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":6.0,\n  \"ndb_no\":9018,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":0.26,\n  \"id\":1874,\n  \"energy_in_kcal\":47.0,\n  \"fat_mono_unsaturated\":0.002,\n  \"description\":\n  \"APPLE JUC,FRZ CONC,UNSWTND,DIL W\\/3 VOLUME H2O WO\\/ VIT C\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":0.063,\n  \"zinc\":0.04,\n  \"riboflavin\":0.015,\n  \"copper\":0.014},\n \"6098\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":10.0,\n  \"pantothenic_acid\":0.623,\n  \"phosphorus\":177.0,\n  \"beta_crytoxanthin\":-1.0,\n  \"thiamin\":0.961,\n  \"selenium\":24.7,\n  \"vitamin_k\":46.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":68.49,\n  \"sugar\":5.74,\n  \"vitamin_b12\":-1.0,\n  \"cholesterol\":1.0,\n  \"retinol\":-1.0,\n  \"potassium\":231.0,\n  \"fat_poly_unsaturated\":2.3,\n  \"lipid_total\":5.48,\n  \"prompt\":\"BREAD CRUMBS,DRY,GRATED,SEASONED -- 1 cup\",\n  \"vitamin_e\":0.26,\n  \"vitamin_b6\":0.171,\n  \"fiber\":4.9,\n  \"vitamin_c\":2.7,\n  \"protein\":14.13,\n  \"vitamin_a\":193.0,\n  \"niacin\":6.161,\n  \"fat_saturated\":1.391,\n  \"weight1_gram\":120.0,\n  \"folate_dfe\":188.0,\n  \"choline\":-1.0,\n  \"sodium\":1759.0,\n  \"magnesium\":46.0,\n  \"folic_acid\":99.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":182.0,\n  \"ndb_no\":18376,\n  \"folate_total\":119.0,\n  \"lutein_zeaxanthin\":46.0,\n  \"iron\":4.92,\n  \"id\":6098,\n  \"energy_in_kcal\":383.0,\n  \"fat_mono_unsaturated\":1.168,\n  \"description\":\"BREAD CRUMBS,DRY,GRATED,SEASONED\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":20.0,\n  \"manganese\":0.984,\n  \"zinc\":1.43,\n  \"riboflavin\":0.415,\n  \"copper\":0.244},\n \"4531\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":1.06,\n  \"phosphorus\":358.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.073,\n  \"selenium\":5.6,\n  \"vitamin_k\":0.6,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":19.56,\n  \"sugar\":9.22,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":649.0,\n  \"fat_poly_unsaturated\":14.161,\n  \"lipid_total\":50.39,\n  \"prompt\":\"PEANUT BUTTER,SMOOTH STYLE,W\\/ SALT -- 1 cup\",\n  \"vitamin_e\":8.99,\n  \"vitamin_b6\":0.543,\n  \"fiber\":6.0,\n  \"vitamin_c\":0.0,\n  \"protein\":25.09,\n  \"vitamin_a\":0.0,\n  \"niacin\":13.403,\n  \"fat_saturated\":10.51,\n  \"weight1_gram\":258.0,\n  \"folate_dfe\":74.0,\n  \"choline\":63.0,\n  \"sodium\":459.0,\n  \"magnesium\":154.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":43.0,\n  \"ndb_no\":16098,\n  \"folate_total\":74.0,\n  \"lutein_zeaxanthin\":0.6,\n  \"iron\":1.87,\n  \"id\":4531,\n  \"energy_in_kcal\":588.0,\n  \"fat_mono_unsaturated\":24.216,\n  \"description\":\"PEANUT BUTTER,SMOOTH STYLE,W\\/ SALT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":74.0,\n  \"manganese\":1.466,\n  \"zinc\":2.91,\n  \"riboflavin\":0.105,\n  \"copper\":0.473},\n \"691\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":27.0,\n  \"pantothenic_acid\":0.389,\n  \"phosphorus\":95.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.029,\n  \"selenium\":2.2,\n  \"vitamin_k\":0.2,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":4.66,\n  \"sugar\":4.66,\n  \"vitamin_b12\":0.37,\n  \"cholesterol\":13.0,\n  \"retinol\":27.0,\n  \"potassium\":155.0,\n  \"fat_poly_unsaturated\":0.092,\n  \"lipid_total\":3.25,\n  \"prompt\":\n  \"YOGURT,PLN,WHL MILK,8 GRAMS PROT PER 8 OZ -- 1 cup,  (8 fl oz)\",\n  \"vitamin_e\":0.06,\n  \"vitamin_b6\":0.032,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.5,\n  \"protein\":3.47,\n  \"vitamin_a\":99.0,\n  \"niacin\":0.075,\n  \"fat_saturated\":2.096,\n  \"weight1_gram\":245.0,\n  \"folate_dfe\":7.0,\n  \"choline\":15.2,\n  \"sodium\":46.0,\n  \"magnesium\":12.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup,  (8 fl oz)\",\n  \"calcium\":121.0,\n  \"ndb_no\":1116,\n  \"folate_total\":7.0,\n  \"lutein_zeaxanthin\":0.2,\n  \"iron\":0.05,\n  \"id\":691,\n  \"energy_in_kcal\":61.0,\n  \"fat_mono_unsaturated\":0.893,\n  \"description\":\"YOGURT,PLN,WHL MILK,8 GRAMS PROT PER 8 OZ\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":7.0,\n  \"manganese\":0.004,\n  \"zinc\":0.59,\n  \"riboflavin\":0.142,\n  \"copper\":0.009},\n \"4851\":\n {\"beta_carotene\":-1.0,\n  \"lycopene\":-1.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.43,\n  \"phosphorus\":168.0,\n  \"beta_crytoxanthin\":-1.0,\n  \"thiamin\":0.115,\n  \"selenium\":1.2,\n  \"vitamin_k\":-1.0,\n  \"alpha_carotene\":-1.0,\n  \"carbohydrate\":24.77,\n  \"sugar\":-1.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":532.0,\n  \"fat_poly_unsaturated\":-1.0,\n  \"lipid_total\":0.1,\n  \"prompt\":\"BEANS,ADZUKI,MATURE SD,CKD,BLD,W\\/SALT -- 1 cup\",\n  \"vitamin_e\":-1.0,\n  \"vitamin_b6\":0.096,\n  \"fiber\":7.3,\n  \"vitamin_c\":0.0,\n  \"protein\":7.52,\n  \"vitamin_a\":6.0,\n  \"niacin\":0.717,\n  \"fat_saturated\":0.036,\n  \"weight1_gram\":230.0,\n  \"folate_dfe\":121.0,\n  \"choline\":-1.0,\n  \"sodium\":244.0,\n  \"magnesium\":52.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":28.0,\n  \"ndb_no\":16302,\n  \"folate_total\":121.0,\n  \"lutein_zeaxanthin\":-1.0,\n  \"iron\":2.0,\n  \"id\":4851,\n  \"energy_in_kcal\":128.0,\n  \"fat_mono_unsaturated\":-1.0,\n  \"description\":\"BEANS,ADZUKI,MATURE SD,CKD,BLD,W\\/SALT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":121.0,\n  \"manganese\":0.573,\n  \"zinc\":1.77,\n  \"riboflavin\":0.064,\n  \"copper\":0.298},\n \"7155\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.438,\n  \"phosphorus\":97.0,\n  \"beta_crytoxanthin\":79.0,\n  \"thiamin\":0.08,\n  \"selenium\":39.7,\n  \"vitamin_k\":0.3,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":72.53,\n  \"sugar\":0.31,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":100.0,\n  \"fat_poly_unsaturated\":0.727,\n  \"lipid_total\":1.66,\n  \"prompt\":\"WHEAT FLOURS,BREAD,UNENR -- -1\",\n  \"vitamin_e\":0.4,\n  \"vitamin_b6\":0.037,\n  \"fiber\":2.4,\n  \"vitamin_c\":0.0,\n  \"protein\":11.98,\n  \"vitamin_a\":2.0,\n  \"niacin\":1.0,\n  \"fat_saturated\":0.244,\n  \"weight1_gram\":137.0,\n  \"folate_dfe\":33.0,\n  \"choline\":10.4,\n  \"sodium\":2.0,\n  \"magnesium\":25.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup,  unsifted, dipped\",\n  \"calcium\":15.0,\n  \"ndb_no\":20129,\n  \"folate_total\":33.0,\n  \"lutein_zeaxanthin\":0.3,\n  \"iron\":0.9,\n  \"id\":7155,\n  \"energy_in_kcal\":361.0,\n  \"fat_mono_unsaturated\":0.14,\n  \"description\":\"WHEAT FLOURS,BREAD,UNENR\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":33.0,\n  \"manganese\":0.792,\n  \"zinc\":0.85,\n  \"riboflavin\":0.06,\n  \"copper\":0.182},\n \"1044\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.0,\n  \"phosphorus\":0.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.0,\n  \"selenium\":0.0,\n  \"vitamin_k\":15.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.0,\n  \"sugar\":0.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":0.0,\n  \"fat_poly_unsaturated\":63.3,\n  \"lipid_total\":100.0,\n  \"prompt\":\"OIL,VEGETABLE,WALNUT -- 1 cup\",\n  \"vitamin_e\":0.4,\n  \"vitamin_b6\":0.0,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":0.0,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.0,\n  \"fat_saturated\":9.1,\n  \"weight1_gram\":218.0,\n  \"folate_dfe\":0.0,\n  \"choline\":0.4,\n  \"sodium\":0.0,\n  \"magnesium\":0.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":0.0,\n  \"ndb_no\":4528,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":15.0,\n  \"iron\":0.0,\n  \"id\":1044,\n  \"energy_in_kcal\":884.0,\n  \"fat_mono_unsaturated\":22.8,\n  \"description\":\"OIL,VEGETABLE,WALNUT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":-1.0,\n  \"zinc\":0.0,\n  \"riboflavin\":0.0,\n  \"copper\":0.0},\n \"1140\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.0,\n  \"phosphorus\":8.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.0,\n  \"selenium\":0.1,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.93,\n  \"sugar\":0.4,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":73.0,\n  \"fat_poly_unsaturated\":0.0,\n  \"lipid_total\":0.0,\n  \"prompt\":\"VINEGAR,CIDER -- 1 cup\",\n  \"vitamin_e\":0.0,\n  \"vitamin_b6\":0.0,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":0.0,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.0,\n  \"fat_saturated\":0.0,\n  \"weight1_gram\":239.0,\n  \"folate_dfe\":0.0,\n  \"choline\":0.0,\n  \"sodium\":5.0,\n  \"magnesium\":5.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":7.0,\n  \"ndb_no\":2048,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":0.2,\n  \"id\":1140,\n  \"energy_in_kcal\":21.0,\n  \"fat_mono_unsaturated\":0.0,\n  \"description\":\"VINEGAR,CIDER\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":0.249,\n  \"zinc\":0.04,\n  \"riboflavin\":0.0,\n  \"copper\":0.008},\n \"2324\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":2.0,\n  \"pantothenic_acid\":0.217,\n  \"phosphorus\":18.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.03,\n  \"selenium\":0.4,\n  \"vitamin_k\":0.6,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":10.54,\n  \"sugar\":1.69,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":102.0,\n  \"fat_poly_unsaturated\":0.055,\n  \"lipid_total\":0.2,\n  \"prompt\":\"LIMES,RAW -- 1 fruit,  (2\\\" dia)\",\n  \"vitamin_e\":0.22,\n  \"vitamin_b6\":0.043,\n  \"fiber\":2.8,\n  \"vitamin_c\":29.1,\n  \"protein\":0.7,\n  \"vitamin_a\":50.0,\n  \"niacin\":0.2,\n  \"fat_saturated\":0.022,\n  \"weight1_gram\":67.0,\n  \"folate_dfe\":8.0,\n  \"choline\":5.1,\n  \"sodium\":2.0,\n  \"magnesium\":6.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 fruit,  (2\\\" dia)\",\n  \"calcium\":33.0,\n  \"ndb_no\":9159,\n  \"folate_total\":8.0,\n  \"lutein_zeaxanthin\":0.6,\n  \"iron\":0.6,\n  \"id\":2324,\n  \"energy_in_kcal\":30.0,\n  \"fat_mono_unsaturated\":0.019,\n  \"description\":\"LIMES,RAW\",\n  \"refuse_percent\":16.0,\n  \"food_folate\":8.0,\n  \"manganese\":0.008,\n  \"zinc\":0.11,\n  \"riboflavin\":0.02,\n  \"copper\":0.065},\n \"308\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":366.0,\n  \"pantothenic_acid\":0.271,\n  \"phosphorus\":104.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.017,\n  \"selenium\":2.4,\n  \"vitamin_k\":2.9,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":2.66,\n  \"sugar\":0.2,\n  \"vitamin_b12\":0.42,\n  \"cholesterol\":110.0,\n  \"retinol\":359.0,\n  \"potassium\":119.0,\n  \"fat_poly_unsaturated\":1.265,\n  \"lipid_total\":34.87,\n  \"prompt\":\"CHEESE,CREAM -- 1 cup\",\n  \"vitamin_e\":0.3,\n  \"vitamin_b6\":0.047,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":7.55,\n  \"vitamin_a\":1346.0,\n  \"niacin\":0.101,\n  \"fat_saturated\":21.966,\n  \"weight1_gram\":232.0,\n  \"folate_dfe\":13.0,\n  \"choline\":27.2,\n  \"sodium\":296.0,\n  \"magnesium\":6.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":80.0,\n  \"ndb_no\":1017,\n  \"folate_total\":13.0,\n  \"lutein_zeaxanthin\":2.9,\n  \"iron\":1.2,\n  \"id\":308,\n  \"energy_in_kcal\":349.0,\n  \"fat_mono_unsaturated\":9.838,\n  \"description\":\"CHEESE,CREAM\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":13.0,\n  \"manganese\":0.004,\n  \"zinc\":0.54,\n  \"riboflavin\":0.197,\n  \"copper\":0.016},\n \"1492\":\n {\"beta_carotene\":-1.0,\n  \"lycopene\":-1.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.258,\n  \"phosphorus\":100.0,\n  \"beta_crytoxanthin\":-1.0,\n  \"thiamin\":0.274,\n  \"selenium\":1.4,\n  \"vitamin_k\":-1.0,\n  \"alpha_carotene\":-1.0,\n  \"carbohydrate\":30.88,\n  \"sugar\":-1.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":646.0,\n  \"fat_poly_unsaturated\":0.276,\n  \"lipid_total\":0.48,\n  \"prompt\":\n  \"BEANS,PINTO,IMMAT SEEDS,FRZ,CKD,BLD,DRND,WO\\/SALT -- 1 package,  (10 oz) yields\",\n  \"vitamin_e\":-1.0,\n  \"vitamin_b6\":0.194,\n  \"fiber\":8.6,\n  \"vitamin_c\":0.7,\n  \"protein\":9.31,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.632,\n  \"fat_saturated\":0.058,\n  \"weight1_gram\":284.0,\n  \"folate_dfe\":34.0,\n  \"choline\":-1.0,\n  \"sodium\":83.0,\n  \"magnesium\":54.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 package,  (10 oz) yields\",\n  \"calcium\":52.0,\n  \"ndb_no\":11049,\n  \"folate_total\":34.0,\n  \"lutein_zeaxanthin\":-1.0,\n  \"iron\":2.71,\n  \"id\":1492,\n  \"energy_in_kcal\":162.0,\n  \"fat_mono_unsaturated\":0.035,\n  \"description\":\"BEANS,PINTO,IMMAT SEEDS,FRZ,CKD,BLD,DRND,WO\\/SALT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":34.0,\n  \"manganese\":0.493,\n  \"zinc\":0.69,\n  \"riboflavin\":0.108,\n  \"copper\":0.088},\n \"2516\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":16.0,\n  \"pantothenic_acid\":0.153,\n  \"phosphorus\":20.0,\n  \"beta_crytoxanthin\":91.0,\n  \"thiamin\":0.024,\n  \"selenium\":0.1,\n  \"vitamin_k\":2.6,\n  \"alpha_carotene\":67.0,\n  \"carbohydrate\":9.54,\n  \"sugar\":8.39,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":190.0,\n  \"fat_poly_unsaturated\":0.086,\n  \"lipid_total\":0.25,\n  \"prompt\":\"PEACHES,RAW -- 1 cup, slices\",\n  \"vitamin_e\":0.73,\n  \"vitamin_b6\":0.025,\n  \"fiber\":1.5,\n  \"vitamin_c\":6.6,\n  \"protein\":0.91,\n  \"vitamin_a\":326.0,\n  \"niacin\":0.806,\n  \"fat_saturated\":0.019,\n  \"weight1_gram\":153.83,\n  \"folate_dfe\":4.0,\n  \"choline\":6.1,\n  \"sodium\":0.0,\n  \"magnesium\":9.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, slices\",\n  \"calcium\":6.0,\n  \"ndb_no\":9236,\n  \"folate_total\":4.0,\n  \"lutein_zeaxanthin\":2.6,\n  \"iron\":0.25,\n  \"id\":2516,\n  \"energy_in_kcal\":39.0,\n  \"fat_mono_unsaturated\":0.067,\n  \"description\":\"PEACHES,RAW\",\n  \"refuse_percent\":4.0,\n  \"food_folate\":4.0,\n  \"manganese\":0.061,\n  \"zinc\":0.17,\n  \"riboflavin\":0.031,\n  \"copper\":0.068},\n \"2261\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":11.0,\n  \"pantothenic_acid\":1.31,\n  \"phosphorus\":561.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.459,\n  \"selenium\":65.0,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":1.5,\n  \"sugar\":0.0,\n  \"vitamin_b12\":1.3,\n  \"cholesterol\":113.0,\n  \"retinol\":11.0,\n  \"potassium\":591.0,\n  \"fat_poly_unsaturated\":4.447,\n  \"lipid_total\":40.3,\n  \"prompt\":\"PORK,CURED,BACON,CKD,PAN-FRIED -- 1 slice, cooked\",\n  \"vitamin_e\":0.3,\n  \"vitamin_b6\":0.389,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":38.34,\n  \"vitamin_a\":37.0,\n  \"niacin\":11.575,\n  \"fat_saturated\":13.291,\n  \"weight1_gram\":7.94,\n  \"folate_dfe\":2.0,\n  \"choline\":130.8,\n  \"sodium\":2428.0,\n  \"magnesium\":36.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 slice, cooked\",\n  \"calcium\":12.0,\n  \"ndb_no\":10862,\n  \"folate_total\":2.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":1.38,\n  \"id\":2261,\n  \"energy_in_kcal\":533.0,\n  \"fat_mono_unsaturated\":17.976,\n  \"description\":\"PORK,CURED,BACON,CKD,PAN-FRIED\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":2.0,\n  \"manganese\":0.022,\n  \"zinc\":3.64,\n  \"riboflavin\":0.277,\n  \"copper\":0.146},\n \"2325\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":2.0,\n  \"pantothenic_acid\":0.123,\n  \"phosphorus\":14.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.025,\n  \"selenium\":0.1,\n  \"vitamin_k\":0.6,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":8.42,\n  \"sugar\":1.69,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":117.0,\n  \"fat_poly_unsaturated\":0.023,\n  \"lipid_total\":0.07,\n  \"prompt\":\"LIME JUICE,RAW -- 1 cup\",\n  \"vitamin_e\":0.22,\n  \"vitamin_b6\":0.038,\n  \"fiber\":0.4,\n  \"vitamin_c\":30.0,\n  \"protein\":0.42,\n  \"vitamin_a\":50.0,\n  \"niacin\":0.142,\n  \"fat_saturated\":0.008,\n  \"weight1_gram\":241.53,\n  \"folate_dfe\":10.0,\n  \"choline\":5.1,\n  \"sodium\":2.0,\n  \"magnesium\":8.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":14.0,\n  \"ndb_no\":9160,\n  \"folate_total\":10.0,\n  \"lutein_zeaxanthin\":0.6,\n  \"iron\":0.09,\n  \"id\":2325,\n  \"energy_in_kcal\":25.0,\n  \"fat_mono_unsaturated\":0.008,\n  \"description\":\"LIME JUICE,RAW\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":10.0,\n  \"manganese\":0.018,\n  \"zinc\":0.08,\n  \"riboflavin\":0.015,\n  \"copper\":0.027},\n \"373\":\n {\"beta_carotene\":10515.0,\n  \"lycopene\":10515.0,\n  \"vitamin_a_rae\":15.0,\n  \"pantothenic_acid\":0.007,\n  \"phosphorus\":31.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.022,\n  \"selenium\":0.9,\n  \"vitamin_k\":4.5,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":6.26,\n  \"sugar\":3.06,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":297.0,\n  \"fat_poly_unsaturated\":0.115,\n  \"lipid_total\":0.16,\n  \"prompt\":\"SAUCE,SALSA,RTS -- 1 cup\",\n  \"vitamin_e\":1.18,\n  \"vitamin_b6\":0.175,\n  \"fiber\":1.6,\n  \"vitamin_c\":1.9,\n  \"protein\":1.54,\n  \"vitamin_a\":292.0,\n  \"niacin\":0.075,\n  \"fat_saturated\":0.03,\n  \"weight1_gram\":259.0,\n  \"folate_dfe\":4.0,\n  \"choline\":11.7,\n  \"sodium\":600.0,\n  \"magnesium\":15.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":27.0,\n  \"ndb_no\":6164,\n  \"folate_total\":4.0,\n  \"lutein_zeaxanthin\":4.5,\n  \"iron\":0.46,\n  \"id\":373,\n  \"energy_in_kcal\":27.0,\n  \"fat_mono_unsaturated\":0.025,\n  \"description\":\"SAUCE,SALSA,RTS\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":4.0,\n  \"manganese\":0.121,\n  \"zinc\":0.37,\n  \"riboflavin\":0.032,\n  \"copper\":0.132},\n \"597\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":7.0,\n  \"pantothenic_acid\":0.275,\n  \"phosphorus\":89.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.034,\n  \"selenium\":2.0,\n  \"vitamin_k\":0.1,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":4.79,\n  \"sugar\":4.79,\n  \"vitamin_b12\":0.22,\n  \"cholesterol\":4.0,\n  \"retinol\":7.0,\n  \"potassium\":151.0,\n  \"fat_poly_unsaturated\":0.033,\n  \"lipid_total\":0.88,\n  \"prompt\":\"MILK,BTTRMLK,FLUID,CULTURED,LOWFAT -- 1 cup\",\n  \"vitamin_e\":0.05,\n  \"vitamin_b6\":0.034,\n  \"fiber\":0.0,\n  \"vitamin_c\":1.0,\n  \"protein\":3.31,\n  \"vitamin_a\":26.0,\n  \"niacin\":0.058,\n  \"fat_saturated\":0.548,\n  \"weight1_gram\":245.0,\n  \"folate_dfe\":5.0,\n  \"choline\":16.0,\n  \"sodium\":105.0,\n  \"magnesium\":11.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":116.0,\n  \"ndb_no\":1088,\n  \"folate_total\":5.0,\n  \"lutein_zeaxanthin\":0.1,\n  \"iron\":0.05,\n  \"id\":597,\n  \"energy_in_kcal\":40.0,\n  \"fat_mono_unsaturated\":0.254,\n  \"description\":\"MILK,BTTRMLK,FLUID,CULTURED,LOWFAT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":5.0,\n  \"manganese\":0.002,\n  \"zinc\":0.42,\n  \"riboflavin\":0.154,\n  \"copper\":0.011},\n \"4725\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.0,\n  \"phosphorus\":0.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.059,\n  \"selenium\":0.7,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":76.79,\n  \"sugar\":26.77,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":1.0,\n  \"fat_poly_unsaturated\":0.0,\n  \"lipid_total\":0.2,\n  \"prompt\":\"SYRUPS,CORN,LT -- 1 cup\",\n  \"vitamin_e\":0.0,\n  \"vitamin_b6\":0.0,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":0.0,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.0,\n  \"fat_saturated\":0.0,\n  \"weight1_gram\":340.55,\n  \"folate_dfe\":0.0,\n  \"choline\":0.0,\n  \"sodium\":62.0,\n  \"magnesium\":1.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":13.0,\n  \"ndb_no\":19350,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":0.0,\n  \"id\":4725,\n  \"energy_in_kcal\":283.0,\n  \"fat_mono_unsaturated\":0.0,\n  \"description\":\"SYRUPS,CORN,LT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":0.0,\n  \"zinc\":0.44,\n  \"riboflavin\":0.0,\n  \"copper\":0.0},\n \"4853\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.26,\n  \"phosphorus\":152.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.225,\n  \"selenium\":1.2,\n  \"vitamin_k\":3.3,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":24.35,\n  \"sugar\":0.32,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":433.0,\n  \"fat_poly_unsaturated\":0.149,\n  \"lipid_total\":0.35,\n  \"prompt\":\n  \"BEANS,BLACK TURTLE SOUP,MATURE SEEDS,CKD,BLD,W\\/SALT -- 1 cup\",\n  \"vitamin_e\":0.87,\n  \"vitamin_b6\":0.077,\n  \"fiber\":5.3,\n  \"vitamin_c\":0.0,\n  \"protein\":8.18,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.527,\n  \"fat_saturated\":0.089,\n  \"weight1_gram\":185.0,\n  \"folate_dfe\":86.0,\n  \"choline\":32.6,\n  \"sodium\":239.0,\n  \"magnesium\":49.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":55.0,\n  \"ndb_no\":16317,\n  \"folate_total\":86.0,\n  \"lutein_zeaxanthin\":3.3,\n  \"iron\":2.85,\n  \"id\":4853,\n  \"energy_in_kcal\":130.0,\n  \"fat_mono_unsaturated\":0.03,\n  \"description\":\n  \"BEANS,BLACK TURTLE SOUP,MATURE SEEDS,CKD,BLD,W\\/SALT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":86.0,\n  \"manganese\":0.327,\n  \"zinc\":0.76,\n  \"riboflavin\":0.056,\n  \"copper\":0.269},\n \"7061\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":1.008,\n  \"phosphorus\":346.0,\n  \"beta_crytoxanthin\":220.0,\n  \"thiamin\":0.447,\n  \"selenium\":70.7,\n  \"vitamin_k\":1.9,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":72.57,\n  \"sugar\":0.41,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":405.0,\n  \"fat_poly_unsaturated\":0.779,\n  \"lipid_total\":1.87,\n  \"prompt\":\"WHEAT FLOUR,WHOLE-GRAIN -- 1 cup\",\n  \"vitamin_e\":0.82,\n  \"vitamin_b6\":0.341,\n  \"fiber\":12.2,\n  \"vitamin_c\":0.0,\n  \"protein\":13.7,\n  \"vitamin_a\":9.0,\n  \"niacin\":6.365,\n  \"fat_saturated\":0.322,\n  \"weight1_gram\":120.0,\n  \"folate_dfe\":44.0,\n  \"choline\":31.2,\n  \"sodium\":5.0,\n  \"magnesium\":138.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":34.0,\n  \"ndb_no\":20080,\n  \"folate_total\":44.0,\n  \"lutein_zeaxanthin\":1.9,\n  \"iron\":3.88,\n  \"id\":7061,\n  \"energy_in_kcal\":339.0,\n  \"fat_mono_unsaturated\":0.232,\n  \"description\":\"WHEAT FLOUR,WHOLE-GRAIN\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":44.0,\n  \"manganese\":3.799,\n  \"zinc\":2.93,\n  \"riboflavin\":0.215,\n  \"copper\":0.382},\n \"150\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":25.0,\n  \"pantothenic_acid\":0.804,\n  \"phosphorus\":174.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.063,\n  \"selenium\":16.6,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.0,\n  \"sugar\":0.0,\n  \"vitamin_b12\":0.34,\n  \"cholesterol\":64.0,\n  \"retinol\":24.0,\n  \"potassium\":220.0,\n  \"fat_poly_unsaturated\":1.96,\n  \"lipid_total\":9.25,\n  \"prompt\":\n  \"CHICKEN,BROILERS OR FRYERS,BREAST,MEAT&SKN,RAW -- .5 breast, bone removed (yield from 1 lb ready-to-cook chicken)\",\n  \"vitamin_e\":0.31,\n  \"vitamin_b6\":0.53,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":20.85,\n  \"vitamin_a\":83.0,\n  \"niacin\":9.908,\n  \"fat_saturated\":2.66,\n  \"weight1_gram\":87.0,\n  \"folate_dfe\":4.0,\n  \"choline\":67.1,\n  \"sodium\":63.0,\n  \"magnesium\":25.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\n  \".5 breast, bone removed (yield from 1 lb ready-to-cook chicken)\",\n  \"calcium\":11.0,\n  \"ndb_no\":5057,\n  \"folate_total\":4.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":0.74,\n  \"id\":150,\n  \"energy_in_kcal\":172.0,\n  \"fat_mono_unsaturated\":3.82,\n  \"description\":\"CHICKEN,BROILERS OR FRYERS,BREAST,MEAT&SKN,RAW\",\n  \"refuse_percent\":20.0,\n  \"food_folate\":4.0,\n  \"manganese\":0.018,\n  \"zinc\":0.8,\n  \"riboflavin\":0.085,\n  \"copper\":0.039},\n \"241\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":25.0,\n  \"pantothenic_acid\":0.804,\n  \"phosphorus\":174.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.063,\n  \"selenium\":16.6,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.0,\n  \"sugar\":0.0,\n  \"vitamin_b12\":0.34,\n  \"cholesterol\":64.0,\n  \"retinol\":24.0,\n  \"potassium\":220.0,\n  \"fat_poly_unsaturated\":1.96,\n  \"lipid_total\":9.25,\n  \"prompt\":\n  \"CHICKEN,BROILERS OR FRYERS,BREAST,MEAT&SKN,RAW -- 1 leg, bone removed (yield from 1 lb ready-to-cook chicken)\",\n  \"vitamin_e\":0.31,\n  \"vitamin_b6\":0.53,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":20.85,\n  \"vitamin_a\":83.0,\n  \"niacin\":9.908,\n  \"fat_saturated\":2.66,\n  \"weight1_gram\":87.0,\n  \"folate_dfe\":4.0,\n  \"choline\":67.1,\n  \"sodium\":63.0,\n  \"magnesium\":25.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\n  \"1 leg, bone removed (yield from 1 lb ready-to-cook chicken)\",\n  \"calcium\":11.0,\n  \"ndb_no\":5057,\n  \"folate_total\":4.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":0.74,\n  \"id\":150,\n  \"energy_in_kcal\":172.0,\n  \"fat_mono_unsaturated\":3.82,\n  \"description\":\"CHICKEN,LEG,RAW\",\n  \"refuse_percent\":20.0,\n  \"food_folate\":4.0,\n  \"manganese\":0.018,\n  \"zinc\":0.8,\n  \"riboflavin\":0.085,\n  \"copper\":0.039},\n \"2422\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":11.0,\n  \"pantothenic_acid\":0.25,\n  \"phosphorus\":12.0,\n  \"beta_crytoxanthin\":129.0,\n  \"thiamin\":0.1,\n  \"selenium\":0.5,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":116.0,\n  \"carbohydrate\":11.54,\n  \"sugar\":9.14,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":169.0,\n  \"fat_poly_unsaturated\":0.042,\n  \"lipid_total\":0.21,\n  \"prompt\":\"ORANGES,RAW,FLORIDA -- 1 cup, sections without membranes\",\n  \"vitamin_e\":0.18,\n  \"vitamin_b6\":0.051,\n  \"fiber\":2.4,\n  \"vitamin_c\":45.0,\n  \"protein\":0.7,\n  \"vitamin_a\":225.0,\n  \"niacin\":0.4,\n  \"fat_saturated\":0.025,\n  \"weight1_gram\":185.0,\n  \"folate_dfe\":17.0,\n  \"choline\":8.4,\n  \"sodium\":0.0,\n  \"magnesium\":10.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, sections without membranes\",\n  \"calcium\":43.0,\n  \"ndb_no\":9203,\n  \"folate_total\":17.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":0.09,\n  \"id\":2422,\n  \"energy_in_kcal\":46.0,\n  \"fat_mono_unsaturated\":0.039,\n  \"description\":\"ORANGES,RAW,FLORIDA\",\n  \"refuse_percent\":26.0,\n  \"food_folate\":17.0,\n  \"manganese\":0.024,\n  \"zinc\":0.08,\n  \"riboflavin\":0.04,\n  \"copper\":0.039},\n \"5526\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":54.0,\n  \"pantothenic_acid\":0.276,\n  \"phosphorus\":205.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.028,\n  \"selenium\":38.0,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.91,\n  \"sugar\":0.0,\n  \"vitamin_b12\":1.16,\n  \"cholesterol\":152.0,\n  \"retinol\":54.0,\n  \"potassium\":185.0,\n  \"fat_poly_unsaturated\":0.669,\n  \"lipid_total\":1.73,\n  \"prompt\":\"SHRIMP,MIXED SPECIES,RAW -- 1 medium\",\n  \"vitamin_e\":1.1,\n  \"vitamin_b6\":0.104,\n  \"fiber\":0.0,\n  \"vitamin_c\":2.0,\n  \"protein\":20.31,\n  \"vitamin_a\":180.0,\n  \"niacin\":2.552,\n  \"fat_saturated\":0.328,\n  \"weight1_gram\":6.0,\n  \"folate_dfe\":3.0,\n  \"choline\":80.9,\n  \"sodium\":148.0,\n  \"magnesium\":37.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 medium\",\n  \"calcium\":52.0,\n  \"ndb_no\":15149,\n  \"folate_total\":3.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":2.41,\n  \"id\":5526,\n  \"energy_in_kcal\":106.0,\n  \"fat_mono_unsaturated\":0.253,\n  \"description\":\"SHRIMP,MIXED SPECIES,RAW\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":3.0,\n  \"manganese\":0.05,\n  \"zinc\":1.11,\n  \"riboflavin\":0.034,\n  \"copper\":0.264},\n \"1494\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":35.0,\n  \"pantothenic_acid\":0.094,\n  \"phosphorus\":38.0,\n  \"beta_crytoxanthin\":640.0,\n  \"thiamin\":0.084,\n  \"selenium\":0.6,\n  \"vitamin_k\":14.4,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":7.13,\n  \"sugar\":1.4,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":209.0,\n  \"fat_poly_unsaturated\":0.059,\n  \"lipid_total\":0.12,\n  \"prompt\":\"BEANS,SNAP,GREEN,RAW -- 1 cup\",\n  \"vitamin_e\":0.41,\n  \"vitamin_b6\":0.074,\n  \"fiber\":3.4,\n  \"vitamin_c\":16.3,\n  \"protein\":1.82,\n  \"vitamin_a\":690.0,\n  \"niacin\":0.752,\n  \"fat_saturated\":0.026,\n  \"weight1_gram\":110.0,\n  \"folate_dfe\":37.0,\n  \"choline\":15.3,\n  \"sodium\":6.0,\n  \"magnesium\":25.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":37.0,\n  \"ndb_no\":11052,\n  \"folate_total\":37.0,\n  \"lutein_zeaxanthin\":14.4,\n  \"iron\":1.04,\n  \"id\":1494,\n  \"energy_in_kcal\":31.0,\n  \"fat_mono_unsaturated\":0.005,\n  \"description\":\"BEANS,SNAP,GREEN,RAW\",\n  \"refuse_percent\":12.0,\n  \"food_folate\":37.0,\n  \"manganese\":0.214,\n  \"zinc\":0.24,\n  \"riboflavin\":0.105,\n  \"copper\":0.069},\n \"4598\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.068,\n  \"phosphorus\":4.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.0,\n  \"selenium\":0.8,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":82.4,\n  \"sugar\":82.12,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":52.0,\n  \"fat_poly_unsaturated\":0.0,\n  \"lipid_total\":0.0,\n  \"prompt\":\"HONEY -- 1 cup\",\n  \"vitamin_e\":0.0,\n  \"vitamin_b6\":0.024,\n  \"fiber\":0.2,\n  \"vitamin_c\":0.5,\n  \"protein\":0.3,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.121,\n  \"fat_saturated\":0.0,\n  \"weight1_gram\":339.0,\n  \"folate_dfe\":2.0,\n  \"choline\":2.2,\n  \"sodium\":4.0,\n  \"magnesium\":2.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":6.0,\n  \"ndb_no\":19296,\n  \"folate_total\":2.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":0.42,\n  \"id\":4598,\n  \"energy_in_kcal\":304.0,\n  \"fat_mono_unsaturated\":0.0,\n  \"description\":\"HONEY\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":2.0,\n  \"manganese\":0.08,\n  \"zinc\":0.22,\n  \"riboflavin\":0.038,\n  \"copper\":0.036},\n \"4151\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":469.0,\n  \"pantothenic_acid\":0.065,\n  \"phosphorus\":49.0,\n  \"beta_crytoxanthin\":12198.0,\n  \"thiamin\":0.078,\n  \"selenium\":1.0,\n  \"vitamin_k\":482.9,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":3.63,\n  \"sugar\":0.42,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":558.0,\n  \"fat_poly_unsaturated\":0.165,\n  \"lipid_total\":0.39,\n  \"prompt\":\"SPINACH,RAW -- 1 cup\",\n  \"vitamin_e\":2.03,\n  \"vitamin_b6\":0.195,\n  \"fiber\":2.2,\n  \"vitamin_c\":28.1,\n  \"protein\":2.86,\n  \"vitamin_a\":9377.0,\n  \"niacin\":0.724,\n  \"fat_saturated\":0.063,\n  \"weight1_gram\":30.0,\n  \"folate_dfe\":194.0,\n  \"choline\":18.0,\n  \"sodium\":79.0,\n  \"magnesium\":79.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":99.0,\n  \"ndb_no\":11457,\n  \"folate_total\":194.0,\n  \"lutein_zeaxanthin\":482.9,\n  \"iron\":2.71,\n  \"id\":4151,\n  \"energy_in_kcal\":23.0,\n  \"fat_mono_unsaturated\":0.01,\n  \"description\":\"SPINACH,RAW\",\n  \"refuse_percent\":28.0,\n  \"food_folate\":194.0,\n  \"manganese\":0.897,\n  \"zinc\":0.53,\n  \"riboflavin\":0.189,\n  \"copper\":0.13},\n \"4631\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.376,\n  \"phosphorus\":130.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.059,\n  \"selenium\":0.8,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":5.57,\n  \"sugar\":1.7,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":212.0,\n  \"fat_poly_unsaturated\":0.044,\n  \"lipid_total\":0.1,\n  \"prompt\":\"SOY SAU MADE FROM SOY (TAMARI) -- 1 tbsp\",\n  \"vitamin_e\":0.0,\n  \"vitamin_b6\":0.2,\n  \"fiber\":0.8,\n  \"vitamin_c\":0.0,\n  \"protein\":10.51,\n  \"vitamin_a\":0.0,\n  \"niacin\":3.951,\n  \"fat_saturated\":0.011,\n  \"weight1_gram\":18.0,\n  \"folate_dfe\":18.0,\n  \"choline\":38.4,\n  \"sodium\":5586.0,\n  \"magnesium\":40.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 tbsp\",\n  \"calcium\":20.0,\n  \"ndb_no\":16124,\n  \"folate_total\":18.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":2.38,\n  \"id\":4631,\n  \"energy_in_kcal\":60.0,\n  \"fat_mono_unsaturated\":0.017,\n  \"description\":\"SOY SAU MADE FROM SOY (TAMARI)\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":18.0,\n  \"manganese\":0.499,\n  \"zinc\":0.43,\n  \"riboflavin\":0.152,\n  \"copper\":0.135},\n \"463199\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.0,\n  \"phosphorus\":0.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.0,\n  \"selenium\":0.0,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.0,\n  \"sugar\":0.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":0.0,\n  \"fat_poly_unsaturated\":0.0,\n  \"lipid_total\":0.0,\n  \"prompt\":\"table salt -- 1 tbsp\",\n  \"vitamin_e\":0.0,\n  \"vitamin_b6\":0.0,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":0.0,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.0,\n  \"fat_saturated\":0.0,\n  \"weight1_gram\":0.0,\n  \"folate_dfe\":0.0,\n  \"choline\":0.0,\n  \"sodium\":33516.0,\n  \"magnesium\":0.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 tbsp\",\n  \"calcium\":0.0,\n  \"ndb_no\":16124,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":0.0,\n  \"id\":463199,\n  \"energy_in_kcal\":0.0,\n  \"fat_mono_unsaturated\":0.0,\n  \"description\":\"SALT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":0.0,\n  \"zinc\":0.0,\n  \"riboflavin\":0.0,\n  \"copper\":0.0},\n \"1144\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.011,\n  \"phosphorus\":0.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.003,\n  \"selenium\":0.0,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":14.4,\n  \"sugar\":14.4,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":0.0,\n  \"fat_poly_unsaturated\":0.0,\n  \"lipid_total\":0.0,\n  \"prompt\":\"VANILLA EXTRACT,IMITN,NO ALCOHOL -- 1 tbsp\",\n  \"vitamin_e\":0.0,\n  \"vitamin_b6\":0.008,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":0.03,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.129,\n  \"fat_saturated\":0.0,\n  \"weight1_gram\":13.0,\n  \"folate_dfe\":0.0,\n  \"choline\":0.0,\n  \"sodium\":3.0,\n  \"magnesium\":1.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 tbsp\",\n  \"calcium\":3.0,\n  \"ndb_no\":2052,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":0.05,\n  \"id\":1144,\n  \"energy_in_kcal\":56.0,\n  \"fat_mono_unsaturated\":0.0,\n  \"description\":\"VANILLA EXTRACT,IMITN,NO ALCOHOL\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":0.001,\n  \"zinc\":0.01,\n  \"riboflavin\":0.029,\n  \"copper\":0.003},\n \"2424\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":10.0,\n  \"pantothenic_acid\":0.19,\n  \"phosphorus\":17.0,\n  \"beta_crytoxanthin\":115.0,\n  \"thiamin\":0.09,\n  \"selenium\":0.1,\n  \"vitamin_k\":0.1,\n  \"alpha_carotene\":169.0,\n  \"carbohydrate\":10.4,\n  \"sugar\":8.4,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":200.0,\n  \"fat_poly_unsaturated\":0.04,\n  \"lipid_total\":0.2,\n  \"prompt\":\"ORANGE JUICE,RAW -- 1 cup\",\n  \"vitamin_e\":0.04,\n  \"vitamin_b6\":0.04,\n  \"fiber\":0.2,\n  \"vitamin_c\":50.0,\n  \"protein\":0.7,\n  \"vitamin_a\":200.0,\n  \"niacin\":0.4,\n  \"fat_saturated\":0.024,\n  \"weight1_gram\":248.0,\n  \"folate_dfe\":30.0,\n  \"choline\":6.2,\n  \"sodium\":1.0,\n  \"magnesium\":11.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":11.0,\n  \"ndb_no\":9206,\n  \"folate_total\":30.0,\n  \"lutein_zeaxanthin\":0.1,\n  \"iron\":0.2,\n  \"id\":2424,\n  \"energy_in_kcal\":45.0,\n  \"fat_mono_unsaturated\":0.036,\n  \"description\":\"ORANGE JUICE,RAW\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":30.0,\n  \"manganese\":0.014,\n  \"zinc\":0.05,\n  \"riboflavin\":0.03,\n  \"copper\":0.044},\n \"3576\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.123,\n  \"phosphorus\":29.0,\n  \"beta_crytoxanthin\":4.0,\n  \"thiamin\":0.046,\n  \"selenium\":0.5,\n  \"vitamin_k\":0.4,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":9.34,\n  \"sugar\":4.24,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":146.0,\n  \"fat_poly_unsaturated\":0.017,\n  \"lipid_total\":0.1,\n  \"prompt\":\"ONIONS,RAW -- 1 cup, chopped\",\n  \"vitamin_e\":0.02,\n  \"vitamin_b6\":0.12,\n  \"fiber\":1.7,\n  \"vitamin_c\":7.4,\n  \"protein\":1.1,\n  \"vitamin_a\":2.0,\n  \"niacin\":0.116,\n  \"fat_saturated\":0.042,\n  \"weight1_gram\":160.0,\n  \"folate_dfe\":19.0,\n  \"choline\":6.1,\n  \"sodium\":4.0,\n  \"magnesium\":10.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, chopped\",\n  \"calcium\":23.0,\n  \"ndb_no\":11282,\n  \"folate_total\":19.0,\n  \"lutein_zeaxanthin\":0.4,\n  \"iron\":0.21,\n  \"id\":3576,\n  \"energy_in_kcal\":40.0,\n  \"fat_mono_unsaturated\":0.013,\n  \"description\":\"ONIONS,RAW\",\n  \"refuse_percent\":10.0,\n  \"food_folate\":19.0,\n  \"manganese\":0.129,\n  \"zinc\":0.17,\n  \"riboflavin\":0.027,\n  \"copper\":0.039},\n \"357698\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.0,\n  \"phosphorus\":0.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.0,\n  \"selenium\":0.0,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.0,\n  \"sugar\":0.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":0.0,\n  \"fat_poly_unsaturated\":0.0,\n  \"lipid_total\":0.0,\n  \"prompt\":\"GARLIC,RAW -- 1 clove, chopped\",\n  \"vitamin_e\":0.0,\n  \"vitamin_b6\":2.0,\n  \"fiber\":0.0,\n  \"vitamin_c\":2.0,\n  \"protein\":0.0,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.0,\n  \"fat_saturated\":0.0,\n  \"weight1_gram\":0.0,\n  \"folate_dfe\":0.0,\n  \"choline\":0.0,\n  \"sodium\":0.0,\n  \"magnesium\":0.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 clove, chopped\",\n  \"calcium\":1.0,\n  \"ndb_no\":11282,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":0.0,\n  \"id\":357698,\n  \"energy_in_kcal\":0.0,\n  \"fat_mono_unsaturated\":0.0,\n  \"description\":\"GARLIC,RAW\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":0.0,\n  \"zinc\":0.0,\n  \"riboflavin\":0.0,\n  \"copper\":0.0},\n \"1656\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":1.0,\n  \"pantothenic_acid\":0.652,\n  \"phosphorus\":44.0,\n  \"beta_crytoxanthin\":33.0,\n  \"thiamin\":0.057,\n  \"selenium\":0.6,\n  \"vitamin_k\":16.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":5.3,\n  \"sugar\":2.4,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":303.0,\n  \"fat_poly_unsaturated\":0.048,\n  \"lipid_total\":0.1,\n  \"prompt\":\"CAULIFLOWER,RAW -- 1 cup\",\n  \"vitamin_e\":0.08,\n  \"vitamin_b6\":0.222,\n  \"fiber\":2.5,\n  \"vitamin_c\":46.4,\n  \"protein\":1.98,\n  \"vitamin_a\":13.0,\n  \"niacin\":0.526,\n  \"fat_saturated\":0.015,\n  \"weight1_gram\":100.0,\n  \"folate_dfe\":57.0,\n  \"choline\":45.2,\n  \"sodium\":30.0,\n  \"magnesium\":15.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":22.0,\n  \"ndb_no\":11135,\n  \"folate_total\":57.0,\n  \"lutein_zeaxanthin\":16.0,\n  \"iron\":0.44,\n  \"id\":1656,\n  \"energy_in_kcal\":25.0,\n  \"fat_mono_unsaturated\":0.007,\n  \"description\":\"CAULIFLOWER,RAW\",\n  \"refuse_percent\":61.0,\n  \"food_folate\":57.0,\n  \"manganese\":0.156,\n  \"zinc\":0.28,\n  \"riboflavin\":0.063,\n  \"copper\":0.042},\n \"4728\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.036,\n  \"phosphorus\":2.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.006,\n  \"selenium\":0.6,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":67.09,\n  \"sugar\":59.52,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":204.0,\n  \"fat_poly_unsaturated\":0.1,\n  \"lipid_total\":0.2,\n  \"prompt\":\"SYRUPS,MAPLE -- 1 cup\",\n  \"vitamin_e\":0.0,\n  \"vitamin_b6\":0.002,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":0.0,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.03,\n  \"fat_saturated\":0.036,\n  \"weight1_gram\":322.45,\n  \"folate_dfe\":0.0,\n  \"choline\":1.6,\n  \"sodium\":9.0,\n  \"magnesium\":14.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":67.0,\n  \"ndb_no\":19353,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":1.2,\n  \"id\":4728,\n  \"energy_in_kcal\":261.0,\n  \"fat_mono_unsaturated\":0.064,\n  \"description\":\"SYRUPS,MAPLE\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":3.298,\n  \"zinc\":4.16,\n  \"riboflavin\":0.01,\n  \"copper\":0.074},\n \"5752\":\n {\"beta_carotene\":-1.0,\n  \"lycopene\":-1.0,\n  \"vitamin_a_rae\":59.0,\n  \"pantothenic_acid\":1.273,\n  \"phosphorus\":332.0,\n  \"beta_crytoxanthin\":-1.0,\n  \"thiamin\":0.1,\n  \"selenium\":14.1,\n  \"vitamin_k\":-1.0,\n  \"alpha_carotene\":-1.0,\n  \"carbohydrate\":0.0,\n  \"sugar\":-1.0,\n  \"vitamin_b12\":3.17,\n  \"cholesterol\":63.0,\n  \"retinol\":59.0,\n  \"potassium\":460.0,\n  \"fat_poly_unsaturated\":1.963,\n  \"lipid_total\":8.23,\n  \"prompt\":\"SALMON,COHO,FARMED,CKD,DRY HEAT -- 1 fillet\",\n  \"vitamin_e\":-1.0,\n  \"vitamin_b6\":0.568,\n  \"fiber\":0.0,\n  \"vitamin_c\":1.5,\n  \"protein\":24.3,\n  \"vitamin_a\":197.0,\n  \"niacin\":7.393,\n  \"fat_saturated\":1.944,\n  \"weight1_gram\":143.0,\n  \"folate_dfe\":14.0,\n  \"choline\":-1.0,\n  \"sodium\":52.0,\n  \"magnesium\":34.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 fillet\",\n  \"calcium\":12.0,\n  \"ndb_no\":15239,\n  \"folate_total\":14.0,\n  \"lutein_zeaxanthin\":-1.0,\n  \"iron\":0.39,\n  \"id\":5752,\n  \"energy_in_kcal\":178.0,\n  \"fat_mono_unsaturated\":3.618,\n  \"description\":\"SALMON,COHO,FARMED,CKD,DRY HEAT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":14.0,\n  \"manganese\":0.021,\n  \"zinc\":0.47,\n  \"riboflavin\":0.113,\n  \"copper\":0.089},\n \"4088\":\n {\"beta_carotene\":16709.0,\n  \"lycopene\":16709.0,\n  \"vitamin_a_rae\":47.0,\n  \"pantothenic_acid\":0.046,\n  \"phosphorus\":33.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.011,\n  \"selenium\":0.3,\n  \"vitamin_k\":2.8,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":25.15,\n  \"sugar\":22.77,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":382.0,\n  \"fat_poly_unsaturated\":0.126,\n  \"lipid_total\":0.31,\n  \"prompt\":\"CATSUP -- 1 cup\",\n  \"vitamin_e\":1.46,\n  \"vitamin_b6\":0.149,\n  \"fiber\":0.3,\n  \"vitamin_c\":15.1,\n  \"protein\":1.74,\n  \"vitamin_a\":933.0,\n  \"niacin\":1.426,\n  \"fat_saturated\":0.043,\n  \"weight1_gram\":240.0,\n  \"folate_dfe\":10.0,\n  \"choline\":12.5,\n  \"sodium\":1114.0,\n  \"magnesium\":19.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":18.0,\n  \"ndb_no\":11935,\n  \"folate_total\":10.0,\n  \"lutein_zeaxanthin\":2.8,\n  \"iron\":0.51,\n  \"id\":4088,\n  \"energy_in_kcal\":97.0,\n  \"fat_mono_unsaturated\":0.047,\n  \"description\":\"CATSUP\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":10.0,\n  \"manganese\":0.128,\n  \"zinc\":0.26,\n  \"riboflavin\":0.132,\n  \"copper\":0.181},\n \"5849\":\n {\"beta_carotene\":-1.0,\n  \"lycopene\":-1.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.242,\n  \"phosphorus\":140.0,\n  \"beta_crytoxanthin\":-1.0,\n  \"thiamin\":0.244,\n  \"selenium\":1.2,\n  \"vitamin_k\":-1.0,\n  \"alpha_carotene\":-1.0,\n  \"carbohydrate\":23.71,\n  \"sugar\":-1.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":355.0,\n  \"fat_poly_unsaturated\":0.231,\n  \"lipid_total\":0.54,\n  \"prompt\":\"BEANS,BLACK,MATURE SEEDS,CKD,BLD,WO\\/SALT -- 1 cup\",\n  \"vitamin_e\":-1.0,\n  \"vitamin_b6\":0.069,\n  \"fiber\":8.7,\n  \"vitamin_c\":0.0,\n  \"protein\":8.86,\n  \"vitamin_a\":6.0,\n  \"niacin\":0.505,\n  \"fat_saturated\":0.139,\n  \"weight1_gram\":172.0,\n  \"folate_dfe\":149.0,\n  \"choline\":-1.0,\n  \"sodium\":1.0,\n  \"magnesium\":70.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":27.0,\n  \"ndb_no\":16015,\n  \"folate_total\":149.0,\n  \"lutein_zeaxanthin\":-1.0,\n  \"iron\":2.1,\n  \"id\":5849,\n  \"energy_in_kcal\":132.0,\n  \"fat_mono_unsaturated\":0.047,\n  \"description\":\"BEANS,BLACK,MATURE SEEDS,CKD,BLD,WO\\/SALT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":149.0,\n  \"manganese\":0.444,\n  \"zinc\":1.12,\n  \"riboflavin\":0.059,\n  \"copper\":0.209},\n \"4857\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.22,\n  \"phosphorus\":142.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.16,\n  \"selenium\":1.2,\n  \"vitamin_k\":3.3,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":22.8,\n  \"sugar\":0.32,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":403.0,\n  \"fat_poly_unsaturated\":0.275,\n  \"lipid_total\":0.5,\n  \"prompt\":\n  \"BEANS,KIDNEY,ALL TYPES,MATURE SEEDS,CKD,BLD,W\\/SALT -- 1 cup\",\n  \"vitamin_e\":0.87,\n  \"vitamin_b6\":0.12,\n  \"fiber\":6.4,\n  \"vitamin_c\":1.2,\n  \"protein\":8.67,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.578,\n  \"fat_saturated\":0.072,\n  \"weight1_gram\":177.0,\n  \"folate_dfe\":130.0,\n  \"choline\":-1.0,\n  \"sodium\":238.0,\n  \"magnesium\":45.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":28.0,\n  \"ndb_no\":16328,\n  \"folate_total\":130.0,\n  \"lutein_zeaxanthin\":3.3,\n  \"iron\":2.94,\n  \"id\":4857,\n  \"energy_in_kcal\":127.0,\n  \"fat_mono_unsaturated\":0.039,\n  \"description\":\"BEANS,KIDNEY,ALL TYPES,MATURE SEEDS,CKD,BLD,W\\/SALT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":130.0,\n  \"manganese\":0.477,\n  \"zinc\":1.07,\n  \"riboflavin\":0.058,\n  \"copper\":0.242},\n \"1977\":\n {\"beta_carotene\":-1.0,\n  \"lycopene\":-1.0,\n  \"vitamin_a_rae\":84.0,\n  \"pantothenic_acid\":0.2,\n  \"phosphorus\":19.0,\n  \"beta_crytoxanthin\":-1.0,\n  \"thiamin\":0.02,\n  \"selenium\":0.4,\n  \"vitamin_k\":-1.0,\n  \"alpha_carotene\":-1.0,\n  \"carbohydrate\":25.1,\n  \"sugar\":-1.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":229.0,\n  \"fat_poly_unsaturated\":0.02,\n  \"lipid_total\":0.1,\n  \"prompt\":\"APRICOTS,FROZEN,SWEETENED -- 1 cup\",\n  \"vitamin_e\":-1.0,\n  \"vitamin_b6\":0.06,\n  \"fiber\":2.2,\n  \"vitamin_c\":9.0,\n  \"protein\":0.7,\n  \"vitamin_a\":1680.0,\n  \"niacin\":0.8,\n  \"fat_saturated\":0.007,\n  \"weight1_gram\":242.0,\n  \"folate_dfe\":2.0,\n  \"choline\":-1.0,\n  \"sodium\":4.0,\n  \"magnesium\":9.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":10.0,\n  \"ndb_no\":9035,\n  \"folate_total\":2.0,\n  \"lutein_zeaxanthin\":-1.0,\n  \"iron\":0.9,\n  \"id\":1977,\n  \"energy_in_kcal\":98.0,\n  \"fat_mono_unsaturated\":0.044,\n  \"description\":\"APRICOTS,FROZEN,SWEETENED\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":2.0,\n  \"manganese\":0.05,\n  \"zinc\":0.1,\n  \"riboflavin\":0.04,\n  \"copper\":0.064},\n \"4057\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.473,\n  \"phosphorus\":78.0,\n  \"beta_crytoxanthin\":12.0,\n  \"thiamin\":0.098,\n  \"selenium\":0.4,\n  \"vitamin_k\":1.8,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":23.51,\n  \"sugar\":0.2,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":-1.0,\n  \"potassium\":400.0,\n  \"fat_poly_unsaturated\":0.218,\n  \"lipid_total\":3.39,\n  \"prompt\":\n  \"POTATOES,FRENCH FR,STK FRIES,SALT ADDED IN PROC,FRZ,AS PRCH -- 10 strip\",\n  \"vitamin_e\":0.08,\n  \"vitamin_b6\":0.168,\n  \"fiber\":1.9,\n  \"vitamin_c\":18.4,\n  \"protein\":2.19,\n  \"vitamin_a\":4.0,\n  \"niacin\":2.005,\n  \"fat_saturated\":0.688,\n  \"weight1_gram\":153.38,\n  \"folate_dfe\":-1.0,\n  \"choline\":-1.0,\n  \"sodium\":317.0,\n  \"magnesium\":21.0,\n  \"folic_acid\":-1.0,\n  \"weight1_description\":\"10 strip\",\n  \"calcium\":9.0,\n  \"ndb_no\":11411,\n  \"folate_total\":38.0,\n  \"lutein_zeaxanthin\":1.8,\n  \"iron\":0.65,\n  \"id\":4057,\n  \"energy_in_kcal\":133.0,\n  \"fat_mono_unsaturated\":2.025,\n  \"description\":\n  \"POTATOES,FRENCH FR,STK FRIES,SALT ADDED IN PROC,FRZ,AS PRCH\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":38.0,\n  \"manganese\":0.161,\n  \"zinc\":0.36,\n  \"riboflavin\":0.072,\n  \"copper\":0.092},\n \"7226\":\n {\"beta_carotene\":-1.0,\n  \"lycopene\":0.11,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.431,\n  \"phosphorus\":189.0,\n  \"beta_crytoxanthin\":-1.0,\n  \"thiamin\":0.09,\n  \"selenium\":63.2,\n  \"vitamin_k\":0.1,\n  \"alpha_carotene\":-1.0,\n  \"carbohydrate\":74.67,\n  \"sugar\":2.67,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":223.0,\n  \"fat_poly_unsaturated\":0.564,\n  \"lipid_total\":1.51,\n  \"prompt\":\"MACARONI,DRY,UNENRICHED -- 1 cup, elbow shaped\",\n  \"vitamin_e\":0.11,\n  \"vitamin_b6\":0.142,\n  \"fiber\":3.2,\n  \"vitamin_c\":0.0,\n  \"protein\":13.04,\n  \"vitamin_a\":0.0,\n  \"niacin\":1.7,\n  \"fat_saturated\":0.277,\n  \"weight1_gram\":105.0,\n  \"folate_dfe\":18.0,\n  \"choline\":-1.0,\n  \"sodium\":6.0,\n  \"magnesium\":53.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, elbow shaped\",\n  \"calcium\":21.0,\n  \"ndb_no\":20499,\n  \"folate_total\":18.0,\n  \"lutein_zeaxanthin\":0.1,\n  \"iron\":1.3,\n  \"id\":7226,\n  \"energy_in_kcal\":371.0,\n  \"fat_mono_unsaturated\":0.171,\n  \"description\":\"MACARONI,DRY,UNENRICHED\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":18.0,\n  \"manganese\":0.917,\n  \"zinc\":1.41,\n  \"riboflavin\":0.06,\n  \"copper\":0.289},\n \"3354\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":4.0,\n  \"pantothenic_acid\":0.24,\n  \"phosphorus\":21.0,\n  \"beta_crytoxanthin\":16.0,\n  \"thiamin\":0.031,\n  \"selenium\":0.1,\n  \"vitamin_k\":7.2,\n  \"alpha_carotene\":18.0,\n  \"carbohydrate\":2.16,\n  \"sugar\":1.38,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":136.0,\n  \"fat_poly_unsaturated\":0.003,\n  \"lipid_total\":0.16,\n  \"prompt\":\"CUCUMBER,PEELED,RAW -- 1 cup, pared chopped\",\n  \"vitamin_e\":0.03,\n  \"vitamin_b6\":0.051,\n  \"fiber\":0.7,\n  \"vitamin_c\":3.2,\n  \"protein\":0.59,\n  \"vitamin_a\":72.0,\n  \"niacin\":0.037,\n  \"fat_saturated\":0.013,\n  \"weight1_gram\":133.0,\n  \"folate_dfe\":14.0,\n  \"choline\":5.7,\n  \"sodium\":2.0,\n  \"magnesium\":12.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, pared chopped\",\n  \"calcium\":14.0,\n  \"ndb_no\":11206,\n  \"folate_total\":14.0,\n  \"lutein_zeaxanthin\":7.2,\n  \"iron\":0.22,\n  \"id\":3354,\n  \"energy_in_kcal\":12.0,\n  \"fat_mono_unsaturated\":0.002,\n  \"description\":\"CUCUMBER,PEELED,RAW\",\n  \"refuse_percent\":27.0,\n  \"food_folate\":14.0,\n  \"manganese\":0.073,\n  \"zinc\":0.17,\n  \"riboflavin\":0.025,\n  \"copper\":0.071},\n \"410\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":97.0,\n  \"pantothenic_acid\":0.289,\n  \"phosphorus\":95.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.035,\n  \"selenium\":1.8,\n  \"vitamin_k\":1.3,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":4.3,\n  \"sugar\":0.16,\n  \"vitamin_b12\":0.33,\n  \"cholesterol\":37.0,\n  \"retinol\":95.0,\n  \"potassium\":130.0,\n  \"fat_poly_unsaturated\":0.427,\n  \"lipid_total\":11.5,\n  \"prompt\":\"CREAM,FLUID,HALF AND HALF -- 1 cup\",\n  \"vitamin_e\":0.33,\n  \"vitamin_b6\":0.039,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.9,\n  \"protein\":2.96,\n  \"vitamin_a\":354.0,\n  \"niacin\":0.078,\n  \"fat_saturated\":7.158,\n  \"weight1_gram\":242.0,\n  \"folate_dfe\":3.0,\n  \"choline\":18.7,\n  \"sodium\":41.0,\n  \"magnesium\":10.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":105.0,\n  \"ndb_no\":1049,\n  \"folate_total\":3.0,\n  \"lutein_zeaxanthin\":1.3,\n  \"iron\":0.07,\n  \"id\":410,\n  \"energy_in_kcal\":130.0,\n  \"fat_mono_unsaturated\":3.321,\n  \"description\":\"CREAM,FLUID,HALF AND HALF\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":3.0,\n  \"manganese\":0.001,\n  \"zinc\":0.51,\n  \"riboflavin\":0.149,\n  \"copper\":0.01},\n \"4634\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.051,\n  \"phosphorus\":92.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.047,\n  \"selenium\":8.9,\n  \"vitamin_k\":2.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":1.8,\n  \"sugar\":0.7,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":120.0,\n  \"fat_poly_unsaturated\":2.081,\n  \"lipid_total\":3.69,\n  \"prompt\":\n  \"TOFU,SOFT,PREP W\\/CA SULFATE&MAGNESIUM CHLORIDE (NIGARI) -- 1 cup,  (1\\/2\\\" cubes)\",\n  \"vitamin_e\":0.01,\n  \"vitamin_b6\":0.052,\n  \"fiber\":0.2,\n  \"vitamin_c\":0.2,\n  \"protein\":6.55,\n  \"vitamin_a\":7.0,\n  \"niacin\":0.535,\n  \"fat_saturated\":0.533,\n  \"weight1_gram\":248.0,\n  \"folate_dfe\":44.0,\n  \"choline\":27.4,\n  \"sodium\":8.0,\n  \"magnesium\":27.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup,  (1\\/2\\\" cubes)\",\n  \"calcium\":111.0,\n  \"ndb_no\":16127,\n  \"folate_total\":44.0,\n  \"lutein_zeaxanthin\":2.0,\n  \"iron\":1.11,\n  \"id\":4634,\n  \"energy_in_kcal\":61.0,\n  \"fat_mono_unsaturated\":0.814,\n  \"description\":\n  \"TOFU,SOFT,PREP W\\/CA SULFATE&MAGNESIUM CHLORIDE (NIGARI)\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":44.0,\n  \"manganese\":0.389,\n  \"zinc\":0.64,\n  \"riboflavin\":0.037,\n  \"copper\":0.157},\n \"698\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":28.0,\n  \"pantothenic_acid\":0.26,\n  \"phosphorus\":120.0,\n  \"beta_crytoxanthin\":70.0,\n  \"thiamin\":0.012,\n  \"selenium\":6.7,\n  \"vitamin_k\":0.06,\n  \"alpha_carotene\":1.4,\n  \"carbohydrate\":0.18,\n  \"sugar\":0.18,\n  \"vitamin_b12\":0.25,\n  \"cholesterol\":250.0,\n  \"retinol\":30.0,\n  \"potassium\":28.0,\n  \"fat_poly_unsaturated\":0.364,\n  \"lipid_total\":1.9,\n  \"prompt\":\"EGG,WHOLE,RAW,FRESH -- 1 unit\",\n  \"vitamin_e\":0.19,\n  \"vitamin_b6\":0.1,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":60.0,\n  \"vitamin_a\":96.0,\n  \"niacin\":0.012,\n  \"fat_saturated\":3.0,\n  \"weight1_gram\":46.0,\n  \"folate_dfe\":9.0,\n  \"choline\":10.1,\n  \"sodium\":3.0,\n  \"magnesium\":1.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 unit\",\n  \"calcium\":11.0,\n  \"ndb_no\":1123,\n  \"folate_total\":9.0,\n  \"lutein_zeaxanthin\":0.06,\n  \"iron\":0.4,\n  \"id\":698,\n  \"energy_in_kcal\":28.0,\n  \"fat_mono_unsaturated\":0.85,\n  \"description\":\"EGG,WHOLE,RAW,FRESH\",\n  \"refuse_percent\":2.2,\n  \"food_folate\":9.0,\n  \"manganese\":0.008,\n  \"zinc\":0.23,\n  \"riboflavin\":0.4,\n  \"copper\":0.022},\n \"4155\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":586.0,\n  \"pantothenic_acid\":0.094,\n  \"phosphorus\":49.0,\n  \"beta_crytoxanthin\":12651.0,\n  \"thiamin\":0.094,\n  \"selenium\":6.0,\n  \"vitamin_k\":372.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":4.21,\n  \"sugar\":0.65,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":346.0,\n  \"fat_poly_unsaturated\":0.082,\n  \"lipid_total\":0.57,\n  \"prompt\":\"SPINACH,FRZ,CHOPD OR LEAF,UNPREP -- 1 cup\",\n  \"vitamin_e\":2.9,\n  \"vitamin_b6\":0.172,\n  \"fiber\":2.9,\n  \"vitamin_c\":5.5,\n  \"protein\":3.63,\n  \"vitamin_a\":11726.0,\n  \"niacin\":0.507,\n  \"fat_saturated\":0.041,\n  \"weight1_gram\":156.0,\n  \"folate_dfe\":145.0,\n  \"choline\":22.1,\n  \"sodium\":74.0,\n  \"magnesium\":75.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":129.0,\n  \"ndb_no\":11463,\n  \"folate_total\":145.0,\n  \"lutein_zeaxanthin\":372.0,\n  \"iron\":1.89,\n  \"id\":4155,\n  \"energy_in_kcal\":29.0,\n  \"fat_mono_unsaturated\":0.0,\n  \"description\":\"SPINACH,FRZ,CHOPD OR LEAF,UNPREP\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":145.0,\n  \"manganese\":0.704,\n  \"zinc\":0.56,\n  \"riboflavin\":0.224,\n  \"copper\":0.144},\n \"5435\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":6.0,\n  \"pantothenic_acid\":0.124,\n  \"phosphorus\":217.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.008,\n  \"selenium\":65.7,\n  \"vitamin_k\":2.5,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.0,\n  \"sugar\":0.0,\n  \"vitamin_b12\":1.17,\n  \"cholesterol\":42.0,\n  \"retinol\":6.0,\n  \"potassium\":237.0,\n  \"fat_poly_unsaturated\":1.109,\n  \"lipid_total\":2.97,\n  \"prompt\":\"TUNA,WHITE,CND IN H2O,DRND SOL -- 1 can\",\n  \"vitamin_e\":0.85,\n  \"vitamin_b6\":0.217,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":23.62,\n  \"vitamin_a\":20.0,\n  \"niacin\":5.799,\n  \"fat_saturated\":0.792,\n  \"weight1_gram\":172.0,\n  \"folate_dfe\":2.0,\n  \"choline\":29.3,\n  \"sodium\":377.0,\n  \"magnesium\":33.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 can\",\n  \"calcium\":14.0,\n  \"ndb_no\":15126,\n  \"folate_total\":2.0,\n  \"lutein_zeaxanthin\":2.5,\n  \"iron\":0.97,\n  \"id\":5435,\n  \"energy_in_kcal\":128.0,\n  \"fat_mono_unsaturated\":0.784,\n  \"description\":\"TUNA,WHITE,CND IN H2O,DRND SOL\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":2.0,\n  \"manganese\":0.019,\n  \"zinc\":0.48,\n  \"riboflavin\":0.044,\n  \"copper\":0.039},\n \"699\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.19,\n  \"phosphorus\":15.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.004,\n  \"selenium\":20.0,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.73,\n  \"sugar\":0.71,\n  \"vitamin_b12\":0.09,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":163.0,\n  \"fat_poly_unsaturated\":0.0,\n  \"lipid_total\":0.17,\n  \"prompt\":\"EGG,WHITE,RAW,FRESH -- 1 cup\",\n  \"vitamin_e\":0.0,\n  \"vitamin_b6\":0.005,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":10.9,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.105,\n  \"fat_saturated\":0.0,\n  \"weight1_gram\":243.0,\n  \"folate_dfe\":4.0,\n  \"choline\":1.1,\n  \"sodium\":166.0,\n  \"magnesium\":11.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":7.0,\n  \"ndb_no\":1124,\n  \"folate_total\":4.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":0.08,\n  \"id\":699,\n  \"energy_in_kcal\":48.0,\n  \"fat_mono_unsaturated\":0.0,\n  \"description\":\"EGG,WHITE,RAW,FRESH\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":4.0,\n  \"manganese\":0.011,\n  \"zinc\":0.03,\n  \"riboflavin\":0.439,\n  \"copper\":0.023},\n \"7131\":\n {\"beta_carotene\":18604.0,\n  \"lycopene\":18604.0,\n  \"vitamin_a_rae\":21.0,\n  \"pantothenic_acid\":-1.0,\n  \"phosphorus\":32.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.07,\n  \"selenium\":0.6,\n  \"vitamin_k\":3.5,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":8.71,\n  \"sugar\":4.24,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":371.0,\n  \"fat_poly_unsaturated\":0.079,\n  \"lipid_total\":0.2,\n  \"prompt\":\"TOMATO SAU,NO SALT -- 1 cup\",\n  \"vitamin_e\":1.74,\n  \"vitamin_b6\":0.16,\n  \"fiber\":1.5,\n  \"vitamin_c\":13.1,\n  \"protein\":1.3,\n  \"vitamin_a\":427.0,\n  \"niacin\":1.15,\n  \"fat_saturated\":0.028,\n  \"weight1_gram\":244.0,\n  \"folate_dfe\":9.0,\n  \"choline\":12.1,\n  \"sodium\":11.0,\n  \"magnesium\":19.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":14.0,\n  \"ndb_no\":43217,\n  \"folate_total\":9.0,\n  \"lutein_zeaxanthin\":3.5,\n  \"iron\":0.77,\n  \"id\":7131,\n  \"energy_in_kcal\":42.0,\n  \"fat_mono_unsaturated\":0.029,\n  \"description\":\"TOMATO SAU,NO SALT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":9.0,\n  \"manganese\":0.131,\n  \"zinc\":0.25,\n  \"riboflavin\":0.06,\n  \"copper\":0.196},\n \"1084\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":22.0,\n  \"pantothenic_acid\":0.636,\n  \"phosphorus\":160.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.092,\n  \"selenium\":15.8,\n  \"vitamin_k\":2.4,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.0,\n  \"sugar\":0.0,\n  \"vitamin_b12\":0.4,\n  \"cholesterol\":91.0,\n  \"retinol\":22.0,\n  \"potassium\":269.0,\n  \"fat_poly_unsaturated\":0.81,\n  \"lipid_total\":3.33,\n  \"prompt\":\"CHICKEN,CORNISH GAME HENS,MEAT ONLY,RAW -- 1 bird, whole\",\n  \"vitamin_e\":0.21,\n  \"vitamin_b6\":0.385,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.6,\n  \"protein\":20.04,\n  \"vitamin_a\":75.0,\n  \"niacin\":6.744,\n  \"fat_saturated\":0.85,\n  \"weight1_gram\":239.0,\n  \"folate_dfe\":3.0,\n  \"choline\":-1.0,\n  \"sodium\":68.0,\n  \"magnesium\":21.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 bird, whole\",\n  \"calcium\":12.0,\n  \"ndb_no\":5309,\n  \"folate_total\":3.0,\n  \"lutein_zeaxanthin\":2.4,\n  \"iron\":0.74,\n  \"id\":1084,\n  \"energy_in_kcal\":116.0,\n  \"fat_mono_unsaturated\":1.07,\n  \"description\":\"CHICKEN,CORNISH GAME HENS,MEAT ONLY,RAW\",\n  \"refuse_percent\":57.0,\n  \"food_folate\":3.0,\n  \"manganese\":0.017,\n  \"zinc\":1.31,\n  \"riboflavin\":0.216,\n  \"copper\":0.053},\n \"7324\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.39,\n  \"phosphorus\":43.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.02,\n  \"selenium\":7.5,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":28.17,\n  \"sugar\":0.05,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":35.0,\n  \"fat_poly_unsaturated\":0.076,\n  \"lipid_total\":0.28,\n  \"prompt\":\"RICE,WHITE,LONG-GRAIN,REG,CKD,UNENR,WO\\/SALT -- 1 cup\",\n  \"vitamin_e\":0.04,\n  \"vitamin_b6\":0.093,\n  \"fiber\":0.4,\n  \"vitamin_c\":0.0,\n  \"protein\":2.69,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.4,\n  \"fat_saturated\":0.077,\n  \"weight1_gram\":158.0,\n  \"folate_dfe\":3.0,\n  \"choline\":2.1,\n  \"sodium\":1.0,\n  \"magnesium\":12.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":10.0,\n  \"ndb_no\":20445,\n  \"folate_total\":3.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":0.2,\n  \"id\":7324,\n  \"energy_in_kcal\":130.0,\n  \"fat_mono_unsaturated\":0.088,\n  \"description\":\"RICE,WHITE,LONG-GRAIN,REG,CKD,UNENR,WO\\/SALT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":3.0,\n  \"manganese\":0.472,\n  \"zinc\":0.49,\n  \"riboflavin\":0.013,\n  \"copper\":0.069},\n \"4572\":\n {\"beta_carotene\":-1.0,\n  \"lycopene\":-1.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.194,\n  \"phosphorus\":314.0,\n  \"beta_crytoxanthin\":-1.0,\n  \"thiamin\":0.112,\n  \"selenium\":-1.0,\n  \"vitamin_k\":-1.0,\n  \"alpha_carotene\":-1.0,\n  \"carbohydrate\":46.6,\n  \"sugar\":-1.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":154.0,\n  \"fat_poly_unsaturated\":1.122,\n  \"lipid_total\":2.5,\n  \"prompt\":\"TORTILLAS,RTB OR -FRY,CORN,WO\\/ SALT -- 1 oz\",\n  \"vitamin_e\":-1.0,\n  \"vitamin_b6\":0.219,\n  \"fiber\":5.2,\n  \"vitamin_c\":0.0,\n  \"protein\":5.7,\n  \"vitamin_a\":0.0,\n  \"niacin\":1.498,\n  \"fat_saturated\":0.334,\n  \"weight1_gram\":28.35,\n  \"folate_dfe\":183.0,\n  \"choline\":-1.0,\n  \"sodium\":11.0,\n  \"magnesium\":65.0,\n  \"folic_acid\":99.0,\n  \"weight1_description\":\"1 oz\",\n  \"calcium\":175.0,\n  \"ndb_no\":18449,\n  \"folate_total\":114.0,\n  \"lutein_zeaxanthin\":-1.0,\n  \"iron\":1.4,\n  \"id\":4572,\n  \"energy_in_kcal\":222.0,\n  \"fat_mono_unsaturated\":0.649,\n  \"description\":\"TORTILLAS,RTB OR -FRY,CORN,WO\\/ SALT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":15.0,\n  \"manganese\":0.402,\n  \"zinc\":0.94,\n  \"riboflavin\":0.073,\n  \"copper\":0.154},\n \"4604\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.804,\n  \"phosphorus\":31.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.041,\n  \"selenium\":17.8,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":74.73,\n  \"sugar\":55.49,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":1464.0,\n  \"fat_poly_unsaturated\":0.05,\n  \"lipid_total\":0.1,\n  \"prompt\":\"MOLASSES -- 1 cup\",\n  \"vitamin_e\":0.0,\n  \"vitamin_b6\":0.67,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":0.0,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.93,\n  \"fat_saturated\":0.018,\n  \"weight1_gram\":337.16,\n  \"folate_dfe\":0.0,\n  \"choline\":13.3,\n  \"sodium\":37.0,\n  \"magnesium\":242.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":205.0,\n  \"ndb_no\":19304,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":0.0,\n  \"iron\":4.72,\n  \"id\":4604,\n  \"energy_in_kcal\":290.0,\n  \"fat_mono_unsaturated\":0.032,\n  \"description\":\"MOLASSES\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":1.53,\n  \"zinc\":0.29,\n  \"riboflavin\":0.002,\n  \"copper\":0.487},\n \"700\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":381.0,\n  \"pantothenic_acid\":2.99,\n  \"phosphorus\":390.0,\n  \"beta_crytoxanthin\":1094.0,\n  \"thiamin\":0.176,\n  \"selenium\":56.0,\n  \"vitamin_k\":0.7,\n  \"alpha_carotene\":33.0,\n  \"carbohydrate\":3.59,\n  \"sugar\":0.56,\n  \"vitamin_b12\":1.95,\n  \"cholesterol\":1234.0,\n  \"retinol\":371.0,\n  \"potassium\":109.0,\n  \"fat_poly_unsaturated\":4.204,\n  \"lipid_total\":26.54,\n  \"prompt\":\"EGG,YOLK,RAW,FRSH -- 1 cup\",\n  \"vitamin_e\":2.58,\n  \"vitamin_b6\":0.35,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":15.86,\n  \"vitamin_a\":1442.0,\n  \"niacin\":0.024,\n  \"fat_saturated\":9.551,\n  \"weight1_gram\":243.0,\n  \"folate_dfe\":146.0,\n  \"choline\":682.3,\n  \"sodium\":48.0,\n  \"magnesium\":5.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":129.0,\n  \"ndb_no\":1125,\n  \"folate_total\":146.0,\n  \"lutein_zeaxanthin\":0.7,\n  \"iron\":2.73,\n  \"id\":700,\n  \"energy_in_kcal\":317.0,\n  \"fat_mono_unsaturated\":11.738,\n  \"description\":\"EGG,YOLK,RAW,FRSH\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":146.0,\n  \"manganese\":0.055,\n  \"zinc\":2.3,\n  \"riboflavin\":0.528,\n  \"copper\":0.077},\n \"1980\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":7.0,\n  \"pantothenic_acid\":1.463,\n  \"phosphorus\":54.0,\n  \"beta_crytoxanthin\":271.0,\n  \"thiamin\":0.075,\n  \"selenium\":0.4,\n  \"vitamin_k\":21.0,\n  \"alpha_carotene\":27.0,\n  \"carbohydrate\":8.64,\n  \"sugar\":0.3,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":507.0,\n  \"fat_poly_unsaturated\":1.816,\n  \"lipid_total\":15.41,\n  \"prompt\":\"AVOCADOS,RAW,CALIFORNIA -- 1 cup, pureed\",\n  \"vitamin_e\":1.97,\n  \"vitamin_b6\":0.287,\n  \"fiber\":6.8,\n  \"vitamin_c\":8.8,\n  \"protein\":1.96,\n  \"vitamin_a\":147.0,\n  \"niacin\":1.912,\n  \"fat_saturated\":2.126,\n  \"weight1_gram\":230.0,\n  \"folate_dfe\":89.0,\n  \"choline\":14.2,\n  \"sodium\":8.0,\n  \"magnesium\":29.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, pureed\",\n  \"calcium\":13.0,\n  \"ndb_no\":9038,\n  \"folate_total\":89.0,\n  \"lutein_zeaxanthin\":21.0,\n  \"iron\":0.61,\n  \"id\":1980,\n  \"energy_in_kcal\":167.0,\n  \"fat_mono_unsaturated\":9.799,\n  \"description\":\"AVOCADOS,RAW,CALIFORNIA\",\n  \"refuse_percent\":33.0,\n  \"food_folate\":89.0,\n  \"manganese\":0.149,\n  \"zinc\":0.68,\n  \"riboflavin\":0.143,\n  \"copper\":0.17},\n \"3357\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":1.0,\n  \"pantothenic_acid\":0.281,\n  \"phosphorus\":25.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.039,\n  \"selenium\":0.3,\n  \"vitamin_k\":3.5,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":5.7,\n  \"sugar\":2.35,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":230.0,\n  \"fat_poly_unsaturated\":0.076,\n  \"lipid_total\":0.19,\n  \"prompt\":\"EGGPLANT,RAW -- 1 cup, cubes\",\n  \"vitamin_e\":0.3,\n  \"vitamin_b6\":0.084,\n  \"fiber\":3.4,\n  \"vitamin_c\":2.2,\n  \"protein\":1.01,\n  \"vitamin_a\":27.0,\n  \"niacin\":0.649,\n  \"fat_saturated\":0.034,\n  \"weight1_gram\":82.0,\n  \"folate_dfe\":22.0,\n  \"choline\":6.9,\n  \"sodium\":2.0,\n  \"magnesium\":14.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, cubes\",\n  \"calcium\":9.0,\n  \"ndb_no\":11209,\n  \"folate_total\":22.0,\n  \"lutein_zeaxanthin\":3.5,\n  \"iron\":0.24,\n  \"id\":3357,\n  \"energy_in_kcal\":24.0,\n  \"fat_mono_unsaturated\":0.016,\n  \"description\":\"EGGPLANT,RAW\",\n  \"refuse_percent\":19.0,\n  \"food_folate\":22.0,\n  \"manganese\":0.25,\n  \"zinc\":0.16,\n  \"riboflavin\":0.037,\n  \"copper\":0.082},\n \"1341\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.0,\n  \"phosphorus\":0.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.0,\n  \"selenium\":0.0,\n  \"vitamin_k\":197.6,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.0,\n  \"sugar\":0.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":0.0,\n  \"fat_poly_unsaturated\":65.138,\n  \"lipid_total\":100.0,\n  \"prompt\":\"USDA CMDTY FD,OIL,VEG,LO SATURATED FAT -- 1 tbsp\",\n  \"vitamin_e\":9.21,\n  \"vitamin_b6\":0.0,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":0.0,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.0,\n  \"fat_saturated\":7.429,\n  \"weight1_gram\":13.6,\n  \"folate_dfe\":0.0,\n  \"choline\":-1.0,\n  \"sodium\":0.0,\n  \"magnesium\":0.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 tbsp\",\n  \"calcium\":0.0,\n  \"ndb_no\":4670,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":197.6,\n  \"iron\":0.02,\n  \"id\":1341,\n  \"energy_in_kcal\":884.0,\n  \"fat_mono_unsaturated\":22.73,\n  \"description\":\"USDA CMDTY FD,OIL,VEG,LO SATURATED FAT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":-1.0,\n  \"zinc\":0.0,\n  \"riboflavin\":0.0,\n  \"copper\":0.0},\n \"3453\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":769.0,\n  \"pantothenic_acid\":0.091,\n  \"phosphorus\":56.0,\n  \"beta_crytoxanthin\":39550.0,\n  \"thiamin\":0.11,\n  \"selenium\":0.9,\n  \"vitamin_k\":817.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":10.01,\n  \"sugar\":-1.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":447.0,\n  \"fat_poly_unsaturated\":0.338,\n  \"lipid_total\":0.7,\n  \"prompt\":\"KALE,RAW -- 1 cup, chopped\",\n  \"vitamin_e\":0.0,\n  \"vitamin_b6\":0.271,\n  \"fiber\":2.0,\n  \"vitamin_c\":120.0,\n  \"protein\":3.3,\n  \"vitamin_a\":15376.0,\n  \"niacin\":1.0,\n  \"fat_saturated\":0.091,\n  \"weight1_gram\":67.0,\n  \"folate_dfe\":29.0,\n  \"choline\":-1.0,\n  \"sodium\":43.0,\n  \"magnesium\":34.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, chopped\",\n  \"calcium\":135.0,\n  \"ndb_no\":11233,\n  \"folate_total\":29.0,\n  \"lutein_zeaxanthin\":817.0,\n  \"iron\":1.7,\n  \"id\":3453,\n  \"energy_in_kcal\":50.0,\n  \"fat_mono_unsaturated\":0.052,\n  \"description\":\"KALE,RAW\",\n  \"refuse_percent\":39.0,\n  \"food_folate\":29.0,\n  \"manganese\":0.774,\n  \"zinc\":0.44,\n  \"riboflavin\":0.13,\n  \"copper\":0.29},\n \"1501\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":2.0,\n  \"pantothenic_acid\":0.155,\n  \"phosphorus\":40.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.031,\n  \"selenium\":0.7,\n  \"vitamin_k\":0.2,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":9.56,\n  \"sugar\":6.76,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":325.0,\n  \"fat_poly_unsaturated\":0.06,\n  \"lipid_total\":0.17,\n  \"prompt\":\"BEETS,RAW -- 1 cup\",\n  \"vitamin_e\":0.04,\n  \"vitamin_b6\":0.067,\n  \"fiber\":2.8,\n  \"vitamin_c\":4.9,\n  \"protein\":1.61,\n  \"vitamin_a\":33.0,\n  \"niacin\":0.334,\n  \"fat_saturated\":0.027,\n  \"weight1_gram\":136.0,\n  \"folate_dfe\":109.0,\n  \"choline\":6.0,\n  \"sodium\":78.0,\n  \"magnesium\":23.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":16.0,\n  \"ndb_no\":11080,\n  \"folate_total\":109.0,\n  \"lutein_zeaxanthin\":0.2,\n  \"iron\":0.8,\n  \"id\":1501,\n  \"energy_in_kcal\":43.0,\n  \"fat_mono_unsaturated\":0.032,\n  \"description\":\"BEETS,RAW\",\n  \"refuse_percent\":33.0,\n  \"food_folate\":109.0,\n  \"manganese\":0.329,\n  \"zinc\":0.35,\n  \"riboflavin\":0.04,\n  \"copper\":0.075},\n \"4797\":\n {\"beta_carotene\":-1.0,\n  \"lycopene\":-1.0,\n  \"vitamin_a_rae\":-1.0,\n  \"pantothenic_acid\":-1.0,\n  \"phosphorus\":-1.0,\n  \"beta_crytoxanthin\":-1.0,\n  \"thiamin\":-1.0,\n  \"selenium\":-1.0,\n  \"vitamin_k\":-1.0,\n  \"alpha_carotene\":-1.0,\n  \"carbohydrate\":49.6,\n  \"sugar\":-1.0,\n  \"vitamin_b12\":-1.0,\n  \"cholesterol\":-1.0,\n  \"retinol\":-1.0,\n  \"potassium\":-1.0,\n  \"fat_poly_unsaturated\":0.924,\n  \"lipid_total\":6.0,\n  \"prompt\":\n  \"MISSION FOODS,MISSION FLR TORTILLAS,SOFT TACO,8 INCH -- 1 serving\",\n  \"vitamin_e\":-1.0,\n  \"vitamin_b6\":-1.0,\n  \"fiber\":-1.0,\n  \"vitamin_c\":-1.0,\n  \"protein\":8.7,\n  \"vitamin_a\":-1.0,\n  \"niacin\":-1.0,\n  \"fat_saturated\":0.695,\n  \"weight1_gram\":51.0,\n  \"folate_dfe\":-1.0,\n  \"choline\":-1.0,\n  \"sodium\":488.0,\n  \"magnesium\":-1.0,\n  \"folic_acid\":-1.0,\n  \"weight1_description\":\"1 serving\",\n  \"calcium\":191.0,\n  \"ndb_no\":18616,\n  \"folate_total\":-1.0,\n  \"lutein_zeaxanthin\":-1.0,\n  \"iron\":1.99,\n  \"id\":4797,\n  \"energy_in_kcal\":287.0,\n  \"fat_mono_unsaturated\":2.771,\n  \"description\":\n  \"MISSION FOODS,MISSION FLR TORTILLAS,SOFT TACO,8 INCH\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":-1.0,\n  \"manganese\":-1.0,\n  \"zinc\":-1.0,\n  \"riboflavin\":-1.0,\n  \"copper\":-1.0},\n \"6141\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":-1.0,\n  \"phosphorus\":25.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.01,\n  \"selenium\":1.6,\n  \"vitamin_k\":24.7,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.3,\n  \"sugar\":0.3,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":14.0,\n  \"fat_poly_unsaturated\":45.539,\n  \"lipid_total\":77.8,\n  \"prompt\":\"MAYONNAISE DRSNG,NO CHOL -- 1 cup\",\n  \"vitamin_e\":11.79,\n  \"vitamin_b6\":0.01,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":0.0,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.01,\n  \"fat_saturated\":10.784,\n  \"weight1_gram\":239.0,\n  \"folate_dfe\":0.0,\n  \"choline\":0.7,\n  \"sodium\":486.0,\n  \"magnesium\":1.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":7.0,\n  \"ndb_no\":43598,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":24.7,\n  \"iron\":0.23,\n  \"id\":6141,\n  \"energy_in_kcal\":688.0,\n  \"fat_mono_unsaturated\":18.026,\n  \"description\":\"MAYONNAISE DRSNG,NO CHOL\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":-1.0,\n  \"zinc\":0.13,\n  \"riboflavin\":0.06,\n  \"copper\":0.0},\n \"5534\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":90.0,\n  \"pantothenic_acid\":0.362,\n  \"phosphorus\":169.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.08,\n  \"selenium\":24.3,\n  \"vitamin_k\":0.2,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":2.57,\n  \"sugar\":0.0,\n  \"vitamin_b12\":49.44,\n  \"cholesterol\":34.0,\n  \"retinol\":90.0,\n  \"potassium\":314.0,\n  \"fat_poly_unsaturated\":0.282,\n  \"lipid_total\":0.97,\n  \"prompt\":\n  \"CLAM,MIXED SPECIES,RAW -- 1 cup,  (with liquid and clams)\",\n  \"vitamin_e\":0.31,\n  \"vitamin_b6\":0.06,\n  \"fiber\":0.0,\n  \"vitamin_c\":13.0,\n  \"protein\":12.77,\n  \"vitamin_a\":300.0,\n  \"niacin\":1.765,\n  \"fat_saturated\":0.094,\n  \"weight1_gram\":227.0,\n  \"folate_dfe\":16.0,\n  \"choline\":65.0,\n  \"sodium\":56.0,\n  \"magnesium\":9.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup,  (with liquid and clams)\",\n  \"calcium\":46.0,\n  \"ndb_no\":15157,\n  \"folate_total\":16.0,\n  \"lutein_zeaxanthin\":0.2,\n  \"iron\":13.98,\n  \"id\":5534,\n  \"energy_in_kcal\":74.0,\n  \"fat_mono_unsaturated\":0.08,\n  \"description\":\"CLAM,MIXED SPECIES,RAW\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":16.0,\n  \"manganese\":0.5,\n  \"zinc\":1.37,\n  \"riboflavin\":0.213,\n  \"copper\":0.344},\n \"1662\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":22.0,\n  \"pantothenic_acid\":0.246,\n  \"phosphorus\":24.0,\n  \"beta_crytoxanthin\":283.0,\n  \"thiamin\":0.021,\n  \"selenium\":0.4,\n  \"vitamin_k\":29.3,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":2.97,\n  \"sugar\":1.83,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":260.0,\n  \"fat_poly_unsaturated\":0.079,\n  \"lipid_total\":0.17,\n  \"prompt\":\"CELERY,RAW -- 1 cup, chopped\",\n  \"vitamin_e\":0.27,\n  \"vitamin_b6\":0.074,\n  \"fiber\":1.6,\n  \"vitamin_c\":3.1,\n  \"protein\":0.69,\n  \"vitamin_a\":449.0,\n  \"niacin\":0.32,\n  \"fat_saturated\":0.042,\n  \"weight1_gram\":101.4,\n  \"folate_dfe\":36.0,\n  \"choline\":6.1,\n  \"sodium\":80.0,\n  \"magnesium\":11.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, chopped\",\n  \"calcium\":40.0,\n  \"ndb_no\":11143,\n  \"folate_total\":36.0,\n  \"lutein_zeaxanthin\":29.3,\n  \"iron\":0.2,\n  \"id\":1662,\n  \"energy_in_kcal\":16.0,\n  \"fat_mono_unsaturated\":0.032,\n  \"description\":\"CELERY,RAW\",\n  \"refuse_percent\":11.0,\n  \"food_folate\":36.0,\n  \"manganese\":0.103,\n  \"zinc\":0.13,\n  \"riboflavin\":0.057,\n  \"copper\":0.035},\n \"4766\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.616,\n  \"phosphorus\":126.0,\n  \"beta_crytoxanthin\":13.0,\n  \"thiamin\":0.427,\n  \"selenium\":29.7,\n  \"vitamin_k\":0.5,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":61.93,\n  \"sugar\":3.59,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":140.0,\n  \"fat_poly_unsaturated\":0.83,\n  \"lipid_total\":2.14,\n  \"prompt\":\n  \"BREAD, FRENCH OR VIENNA, TSTD (IND SOURDOUGH) -- 1 slice,  small\",\n  \"vitamin_e\":0.19,\n  \"vitamin_b6\":0.096,\n  \"fiber\":3.1,\n  \"vitamin_c\":0.2,\n  \"protein\":13.0,\n  \"vitamin_a\":0.0,\n  \"niacin\":5.38,\n  \"fat_saturated\":0.502,\n  \"weight1_gram\":29.0,\n  \"folate_dfe\":217.0,\n  \"choline\":17.5,\n  \"sodium\":720.0,\n  \"magnesium\":31.0,\n  \"folic_acid\":111.0,\n  \"weight1_description\":\"1 slice,  small\",\n  \"calcium\":47.0,\n  \"ndb_no\":18030,\n  \"folate_total\":140.0,\n  \"lutein_zeaxanthin\":0.5,\n  \"iron\":3.87,\n  \"id\":4766,\n  \"energy_in_kcal\":319.0,\n  \"fat_mono_unsaturated\":0.386,\n  \"description\":\"BREAD, FRENCH OR VIENNA, TSTD (IND SOURDOUGH)\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":29.0,\n  \"manganese\":0.573,\n  \"zinc\":1.05,\n  \"riboflavin\":0.37,\n  \"copper\":0.15},\n \"926\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.0,\n  \"phosphorus\":0.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.0,\n  \"selenium\":0.0,\n  \"vitamin_k\":0.5,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.0,\n  \"sugar\":0.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":0.0,\n  \"fat_poly_unsaturated\":1.8,\n  \"lipid_total\":100.0,\n  \"prompt\":\"VEGETABLE OIL,COCONUT -- 1 tbsp\",\n  \"vitamin_e\":0.09,\n  \"vitamin_b6\":0.0,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":0.0,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.0,\n  \"fat_saturated\":86.5,\n  \"weight1_gram\":13.6,\n  \"folate_dfe\":0.0,\n  \"choline\":0.3,\n  \"sodium\":0.0,\n  \"magnesium\":0.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 tbsp\",\n  \"calcium\":0.0,\n  \"ndb_no\":4047,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":0.5,\n  \"iron\":0.04,\n  \"id\":926,\n  \"energy_in_kcal\":862.0,\n  \"fat_mono_unsaturated\":5.8,\n  \"description\":\"VEGETABLE OIL,COCONUT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":0.0,\n  \"zinc\":0.0,\n  \"riboflavin\":0.0,\n  \"copper\":0.0},\n \"1982\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":3.0,\n  \"pantothenic_acid\":0.334,\n  \"phosphorus\":22.0,\n  \"beta_crytoxanthin\":22.0,\n  \"thiamin\":0.031,\n  \"selenium\":1.0,\n  \"vitamin_k\":0.5,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":22.84,\n  \"sugar\":12.23,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":358.0,\n  \"fat_poly_unsaturated\":0.073,\n  \"lipid_total\":0.33,\n  \"prompt\":\"BANANAS,RAW -- 1 cup, mashed\",\n  \"vitamin_e\":0.1,\n  \"vitamin_b6\":0.367,\n  \"fiber\":2.6,\n  \"vitamin_c\":8.7,\n  \"protein\":1.09,\n  \"vitamin_a\":64.0,\n  \"niacin\":0.665,\n  \"fat_saturated\":0.112,\n  \"weight1_gram\":225.0,\n  \"folate_dfe\":20.0,\n  \"choline\":9.8,\n  \"sodium\":1.0,\n  \"magnesium\":27.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup, mashed\",\n  \"calcium\":5.0,\n  \"ndb_no\":9040,\n  \"folate_total\":20.0,\n  \"lutein_zeaxanthin\":0.5,\n  \"iron\":0.26,\n  \"id\":1982,\n  \"energy_in_kcal\":89.0,\n  \"fat_mono_unsaturated\":0.032,\n  \"description\":\"BANANAS,RAW\",\n  \"refuse_percent\":36.0,\n  \"food_folate\":20.0,\n  \"manganese\":0.27,\n  \"zinc\":0.15,\n  \"riboflavin\":0.073,\n  \"copper\":0.078},\n \"3006\":\n {\"beta_carotene\":-1.0,\n  \"lycopene\":2.53,\n  \"vitamin_a_rae\":5.0,\n  \"pantothenic_acid\":0.737,\n  \"phosphorus\":263.0,\n  \"beta_crytoxanthin\":-1.0,\n  \"thiamin\":0.473,\n  \"selenium\":6.0,\n  \"vitamin_k\":62.0,\n  \"alpha_carotene\":-1.0,\n  \"carbohydrate\":13.01,\n  \"sugar\":3.97,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":392.0,\n  \"fat_poly_unsaturated\":23.584,\n  \"lipid_total\":75.23,\n  \"prompt\":\"PECANS,OIL RSTD,WO\\/SALT -- 1 cup\",\n  \"vitamin_e\":2.53,\n  \"vitamin_b6\":0.187,\n  \"fiber\":9.5,\n  \"vitamin_c\":0.7,\n  \"protein\":9.2,\n  \"vitamin_a\":104.0,\n  \"niacin\":1.2,\n  \"fat_saturated\":7.238,\n  \"weight1_gram\":110.0,\n  \"folate_dfe\":15.0,\n  \"choline\":-1.0,\n  \"sodium\":1.0,\n  \"magnesium\":121.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 cup\",\n  \"calcium\":67.0,\n  \"ndb_no\":12144,\n  \"folate_total\":15.0,\n  \"lutein_zeaxanthin\":-1.0,\n  \"iron\":2.47,\n  \"id\":3006,\n  \"energy_in_kcal\":715.0,\n  \"fat_mono_unsaturated\":40.97,\n  \"description\":\"PECANS,OIL RSTD,WO\\/SALT\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":15.0,\n  \"manganese\":3.7,\n  \"zinc\":4.47,\n  \"riboflavin\":0.11,\n  \"copper\":1.2},\n \"1087\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":17.0,\n  \"pantothenic_acid\":0.545,\n  \"phosphorus\":205.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.074,\n  \"selenium\":33.2,\n  \"vitamin_k\":0.0,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":3.34,\n  \"sugar\":2.04,\n  \"vitamin_b12\":0.4,\n  \"cholesterol\":126.0,\n  \"retinol\":17.0,\n  \"potassium\":190.0,\n  \"fat_poly_unsaturated\":2.359,\n  \"lipid_total\":12.67,\n  \"prompt\":\"CHICKEN,WING,FRZ,GLAZED,BARBECUE FLAV -- 1 piece\",\n  \"vitamin_e\":0.36,\n  \"vitamin_b6\":0.142,\n  \"fiber\":0.6,\n  \"vitamin_c\":0.0,\n  \"protein\":19.67,\n  \"vitamin_a\":58.0,\n  \"niacin\":5.5,\n  \"fat_saturated\":3.321,\n  \"weight1_gram\":29.33,\n  \"folate_dfe\":10.0,\n  \"choline\":-1.0,\n  \"sodium\":615.0,\n  \"magnesium\":20.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 piece\",\n  \"calcium\":28.0,\n  \"ndb_no\":5312,\n  \"folate_total\":10.0,\n  \"lutein_zeaxanthin\":-1.0,\n  \"iron\":2.41,\n  \"id\":1087,\n  \"energy_in_kcal\":211.0,\n  \"fat_mono_unsaturated\":5.766,\n  \"description\":\"CHICKEN,WING,FRZ,GLAZED,BARBECUE FLAV\",\n  \"refuse_percent\":33.0,\n  \"food_folate\":10.0,\n  \"manganese\":0.0,\n  \"zinc\":1.14,\n  \"riboflavin\":0.242,\n  \"copper\":0.039},\n \"927\":\n {\"beta_carotene\":0.0,\n  \"lycopene\":0.0,\n  \"vitamin_a_rae\":0.0,\n  \"pantothenic_acid\":0.0,\n  \"phosphorus\":0.0,\n  \"beta_crytoxanthin\":0.0,\n  \"thiamin\":0.0,\n  \"selenium\":0.0,\n  \"vitamin_k\":60.2,\n  \"alpha_carotene\":0.0,\n  \"carbohydrate\":0.0,\n  \"sugar\":0.0,\n  \"vitamin_b12\":0.0,\n  \"cholesterol\":0.0,\n  \"retinol\":0.0,\n  \"potassium\":1.0,\n  \"fat_poly_unsaturated\":10.523,\n  \"lipid_total\":100.0,\n  \"prompt\":\"OIL,OLIVE,SALAD OR COOKING -- 1 tablespoon\",\n  \"vitamin_e\":14.35,\n  \"vitamin_b6\":0.0,\n  \"fiber\":0.0,\n  \"vitamin_c\":0.0,\n  \"protein\":0.0,\n  \"vitamin_a\":0.0,\n  \"niacin\":0.0,\n  \"fat_saturated\":13.808,\n  \"weight1_gram\":13.5,\n  \"folate_dfe\":0.0,\n  \"choline\":0.3,\n  \"sodium\":2.0,\n  \"magnesium\":0.0,\n  \"folic_acid\":0.0,\n  \"weight1_description\":\"1 tablespoon\",\n  \"calcium\":1.0,\n  \"ndb_no\":4053,\n  \"folate_total\":0.0,\n  \"lutein_zeaxanthin\":60.2,\n  \"iron\":0.56,\n  \"id\":927,\n  \"energy_in_kcal\":884.0,\n  \"fat_mono_unsaturated\":72.961,\n  \"description\":\"OIL,OLIVE,SALAD OR COOKING\",\n  \"refuse_percent\":0.0,\n  \"food_folate\":0.0,\n  \"manganese\":0.0,\n  \"zinc\":0.0,\n  \"riboflavin\":0.0,\n  \"copper\":0.0}}"
  },
  {
    "path": "cooking_recipes/data/meat.json",
    "content": "[\n    {\"name\":\"Fried Chicken\",\n        \"num_served\":3,\n        \"ingredients\":\n            [{\"name\":\"chicken (whole fryer)\",\n                \"amount\":\"1\",\n                \"units\":\"\",\n                \"description\":\"Frying chickens\"},\n                {\"name\":\"flour\", \"amount\":\"1/4\", \"units\":\"cup\", \"description\":\"Flour\"},\n                {\"name\":\"salt\", \"amount\":\"1/2\", \"units\":\"teaspoon\", \"description\":\"Salt\"},\n                {\"name\":\"black pepper\",\n                    \"amount\":\"1/2\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Pepper\"},\n                {\"name\":\"cayenne pepper\",\n                    \"amount\":\"1/2\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Cayenne pepper\"},\n                {\"name\":\"butter\",\n                    \"amount\":\"4\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Butter\"},\n                {\"name\":\"vegetable oil\",\n                    \"amount\":\"8\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Vegetable oil\"}],\n        \"directions\":\n            [\"Mix flour, salt and pepper in bag, add chicken and shake.\",\n                \"Heat oil in a large skillet on high. Brown chicken on skin side.\",\n                \"Turn over the chicken, reducing the heat to medium and brown other side.\",\n                \"Turn over and cook over lower heat until the chicken is tender when stuck with a fork.\",\n                \"Drain off oil and serve.\"]},\n    {\"name\":\"Chicken Schnitzel\",\n        \"num_served\":6,\n        \"ingredients\":\n            [{\"name\":\"chicken breast\",\n                \"amount\":\"2\",\n                \"units\":\"pounds\",\n                \"description\":\"Chicken Breasts\"},\n                {\"name\":\"egg\", \"amount\":\"4\", \"units\":\"unit\", \"description\":\"Eggs\"},\n                {\"name\":\"flour\", \"amount\":\"1.5\", \"units\":\"cup\", \"description\":\"Flour\"},\n                {\"name\":\"bread crumbs\",\n                    \"amount\":\"1\",\n                    \"units\":\"cup\",\n                    \"description\":\"Bread Crumbs\"},\n                {\"name\":\"vegetable oil\",\n                    \"amount\":\"5\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Vegetable oil\"}],\n        \"directions\":\n            [\"Slice Chicken Breasts 2 parts so each piece is as large an area and thin as possible.\",\n                \"Remove skin and any excess fat.\",\n                \"Optional: place on a cutting board and pound flatter.\",\n                \"Dip chicken in beaten eggs, roll in flour, then in bread crumbs.\",\n                \"Heat oil in a (preferably nonstick) frying pan.\",\n                \"Fry the Schnitzel on each side until golden brown and the chicken is done (cut into chicken to make sure it is cooked).\"]},\n\n    {\n        \"name\": \"Barbecued Chicken\",\n        \"num_served\":2,\n        \"ingredients\": [\n            {\n                \"name\": \"chicken breast\",\n                \"amount\": \"2\",\n                \"units\": \"unit\",\n                \"description\": \"Boneless chicken breasts\"\n            },\n            {\n                \"name\": \"soy sauce\",\n                \"amount\": \"2\",\n                \"units\": \"tablespoon\",\n                \"description\": \"Soy sauce\"\n            },\n            {\n                \"name\": \"white wine\",\n                \"amount\": \"2\",\n                \"units\": \"tablespoon\",\n                \"description\": \"White wine\"\n            },\n            {\n                \"name\": \"vegetable oil\",\n                \"amount\": \"1\",\n                \"units\": \"tablespoon\",\n                \"description\": \"Vegetable oil\"\n            },\n            {\n                \"name\": \"cornstarch\",\n                \"amount\": \"1\",\n                \"units\": \"tablespoon\",\n                \"description\": \"Cornstarch\"\n            }\n        ],\n        \"pictures\":\n            [\n              [\"barbecue_chicken\", \"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Prepared Barbequed Chicken<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(photo by Joe Foodie CC BY 2.0)\"]\n            ],\n        \"directions\": [\"Mix together all of the sauce ingredients and pour half the sauce over chicken, coating the chicken on all sides.\",\n            \"Let this stand for a while and then put the chicken on your barbeque (charcoal or gas grill works fine).\",\n            \"Brush the sauce over the top of the chicken and grill for about 10 minutes, then turn over and apply more barbeque sauce on the top\",\n            \"Be careful to not burn the chicken. Before removing from the grill, cut into a thick piece and make sure it is done.\"]\n    },\n\n    {\n        \"name\": \"Brined Barbecued Chicken\",\n        \"num_served\":2,\n        \"ingredients\": [\n            {\n                \"name\": \"chicken breast\",\n                \"amount\": \"2\",\n                \"units\": \"unit\",\n                \"description\": \"Boneless chicken breasts\"\n            },\n            {\n                \"name\": \"soy sauce\",\n                \"amount\": \"4\",\n                \"units\": \"tablespoon\",\n                \"description\": \"Soy sauce\"\n            },\n            {\n                \"name\": \"white wine\",\n                \"amount\": \"4\",\n                \"units\": \"tablespoon\",\n                \"description\": \"White wine\"\n            },\n            {\n                \"name\": \"vegetable oil\",\n                \"amount\": \"2\",\n                \"units\": \"tablespoon\",\n                \"description\": \"Vegetable oil\"\n            },\n            {\n                \"name\": \"cornstarch\",\n                \"amount\": \"2\",\n                \"units\": \"tablespoon\",\n                \"description\": \"Cornstarch\"\n            }\n        ],\n        \"directions\": [\"Mix together all of the sauce ingredients. Put half the sauce in a large pan of cold water, add two additional tablespoons of salt, and stir. Add the chicken and store in the refrigerator overnight.\",\n            \"Before barbequeing, remove the chicken from the brine solution and pour half of the sauce (made the day before) over chicken, coating the chicken on all sides.\",\n            \"Put the chicken on your barbeque (charcoal or gas grill works fine).\",\n            \"Brush the sauce over the top of the chicken and grill for about 10 minutes, then turn over and apply more barbeque sauce on the top\",\n            \"Be careful to not burn the chicken. Before removing from the grill, cut into a thick piece and make sure it is done.\",\n            \"NOTE: it is important to use cold water for brining and immediately refrigerate the brine after adding the chicken.\"]\n    },\n    {\"name\":\"Barbecued Salmon with Basil\",\n        \"num_served\":4,\n        \"ingredients\":\n            [{\"name\":\"salmon (small fillet or steak)\",\n                \"amount\":\"4\",\n                \"units\":\"unit\",\n                \"description\":\n                    \"Salmon steaks\"},\n                {\"name\":\"olive oil\",\n                    \"amount\":\"2\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Olive oil\"},\n                {\"name\":\"dried basil\",\n                    \"amount\":\"1\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Dried and crushed basil\"},\n                {\"name\":\"lemon\",\n                    \"amount\":\"1\",\n                    \"units\":\"\",\n                    \"description\":\"Lemon wedges\"}],\n        \"pictures\":\n            [\n              [\"barbequed_salmon\", \"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Barbequed Salmon<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(photo by Jon Sullivan public domain)\"]\n            ],\n        \"directions\":\n            [\"Mix the juice from half the lemon, olive oil and basil and thoroughly coat the salmon with this mixture.\",\n            \"Cook the salmon over hot coals (or a gas grill) for about 5 minutes per side.\",\n            \"Remove fish from barbeque and test for doneness. Serve with the remaining half lemon cut into thin wedges.\"]},\n    {\"name\":\"Spicy Braised Chicken\",\n        \"num_served\":4,\n        \"ingredients\":\n            [{\"name\":\"red pepper flakes\",\n                \"amount\":\"1\",\n                \"units\":\"teaspoon\",\n                \"description\":\"Red peppers\"},\n                {\"name\":\"vegetable oil\", \"amount\":\"1\", \"units\":\"tablespoon\", \"description\":\"Oil\"},\n                {\"name\":\"salt\", \"amount\":\"1\", \"units\":\"teaspoon\", \"description\":\"Salt\"},\n                {\"name\":\"chicken (boneless)\",\n                    \"amount\":\"1\",\n                    \"units\":\"pound\",\n                    \"description\":\"Chicken meat\"},\n                {\"name\":\"fresh ginger (minced)\",\n                    \"amount\":\"1\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Fresh ginger\"},\n                {\"name\":\"brown sugar\",\n                    \"amount\":\"1\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Brown sugar\"},\n                {\"name\":\"sherry\",\n                    \"amount\":\"2\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Sherry\"},\n                {\"name\":\"cornstarch\",\n                    \"amount\":\"1\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Cornstarch\"},\n                {\"name\":\"soy sauce\",\n                    \"amount\":\"2\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Soy sauce\"}],\n        \"directions\":\n            [\"Using a small bowl, mix the cornstarch to a smooth paste with the soy sauce and set aside.\",\n              \"Cut up the chicken into small bite-size pieces. Over medium high heat add oil and fry the chicken and minced fresh ginger for 1 minute.\",\n                \"Add in the mixed cornstarch and soy sauce and cook for another minute, stiring often.\",\n                \"Add the sugar and sherry and cook for another minute.\",\n                \"Test the chicken for doneness and then serve.\"]} ,\n    {\"name\":\"Crock Pot Lemon Honey Chicken\",\n        \"num_served\":4,\n        \"ingredients\":\n            [{\"name\":\"chicken\",\n                \"amount\":\"1\",\n                \"units\":\"\",\n                \"description\":\"Roaster Chicken\"},\n                {\n                    \"name\": \"water\",\n                    \"amount\": \"1/2\",\n                    \"units\": \"cup\",\n                    \"description\": \"Water\"\n                },\n                {\"name\":\"lemon\", \"amount\":\"2\", \"units\":\"\", \"description\":\"Lemon\"},\n                {\"name\":\"salt\", \"amount\":\"1/2\", \"units\":\"teaspoon\", \"description\":\"Salt\"},\n                {\"name\":\"black pepper\",\n                    \"amount\":\"1/2\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Pepper\"},\n                {\"name\":\"honey\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"cup\",\n                    \"description\":\"Honey\"}],\n        \"directions\":\n            [\"Wash the Chicken and trim off excess fat.\",\n                \"Put the washed chicken in a crock pot and squeeze the juice from one lemon over the chicken.\",\n                \"Then sprinkle the salt and pepper over the chicken, and finally pour the honey over the chicken.\",\n                \"Cook the chicken on low heat until done. After serving the chicken, squeeze some juice from the second lemon over each portion.\"]},\n\n    {\"name\":\"Chicken Cacciatore\",\n        \"num_served\":4,\n        \"ingredients\":\n            [{\"name\":\"chicken breast\",\n                \"amount\":\"2\",\n                \"units\":\"\",\n                \"description\":\"Pieces chicken\"},\n                {\"name\":\"tomatoes\",\n                    \"amount\":\"2\",\n                    \"units\":\"\",\n                    \"description\":\"Crushed tomatoes\"},\n                {\"name\":\"brown sugar\",\n                    \"amount\":\"2\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Sugar\"},\n                {\"name\":\"salt\", \"amount\":\"2\", \"units\":\"teaspoon\", \"description\":\"Salt\"},\n                {\"name\":\"black pepper\",\n                    \"amount\":\"1\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Pepper\"},\n                {\"name\":\"butter\",\n                    \"amount\":\"1\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Butter\"},\n                {\"name\":\"vegetable oil\", \"amount\":\"2\", \"units\":\"tablespoon\", \"description\":\"Oil\"},\n                {\"name\":\"dry rosemary\",\n                    \"amount\":\"1\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Rosemary\"}],\n        \"directions\":\n            [\"Wash the chicken and cook it for 5 minutes a side with butter, oil, and rosemary in a large skillet under medium heat.\",\n                \"Then cut up the tomatoes and mix in with the cooking chicken chicken. Add the basil, sage, rosemary and parsley, sugar, salt, and pepper.\",\n                \"Cover and cook on low to medium heat for about two hours. Check chicken for doneness before serving\"]},\n\n    {\"name\":\"Chicken with Cream of Mushroom Soup\",\n        \"num_served\":2,\n        \"ingredients\":\n            [{\"name\":\"chicken breast\",\n                \"amount\":\"2\",\n                \"units\":\"\",\n                \"description\":\"Pieces chicken\"},\n                {\"name\":\"vegetable oil\", \"amount\":\"1\", \"units\":\"tablespoon\", \"description\":\"Oil\"},\n             {\"name\":\"sour cream\",\n                \"amount\":\"1/2\",\n                \"units\":\"cup\",\n                \"description\":\"Pint sour cream\"},\n                {\"name\":\"salt\", \"amount\":\"1/2\", \"units\":\"teaspoon\", \"description\":\"Salt\"},\n                {\"name\":\"black pepper\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Pepper\"},\n                {\"name\":\"canned soup (cream of mushroom)\",\n                    \"amount\":\"1\",\n                    \"units\":\"can\",\n                    \"description\":\"Cream of mushroom soup (I use Campbell's)\"}],\n        \"directions\":\n            [\"Heat oil in a non-stick frying pan with medium heat. Lightly brown the chicken, about  minutes per side.\",\n                \"Mix in the salt, pepper, and soup, then cover, and cook under low heat for about 30 minutes, stirring occasionally.\",\n                \"Mix in the sour cream, cook for another 5 minutes. Make sure the chicken is done and tender before serving.\"]},\n    {\"name\":\"Simple Italian Chicken\",\n        \"num_served\":4,\n        \"ingredients\":\n            [{\"name\":\"chicken breast (skinless)\",\n                \"amount\":\"2\",\n                \"units\":\"lb\",\n                \"description\":\"Skinned chicken breasts\"},\n                {\"name\":\"olive oil\",\n                    \"amount\":\"2\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Olive oil\"},\n                {\"name\":\"lemon juice\",\n                    \"amount\":\"2\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Lemon juice\"},\n                {\"name\":\"fresh garlic (peeled and finely chopped)\",\n                    \"amount\":\"2\",\n                    \"units\":\"\",\n                    \"description\":\"Cloves garlic,crushed\"},\n                {\"name\":\"dried oregano\",\n                    \"amount\":\"1/2\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Oregano\"},\n                {\"name\":\"white wine\",\n                    \"amount\":\"4\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Dry white wine\"}],\n        \"directions\":\n            [\"Preheat oven to 375F. Grease a deep baking pan with half of the olive oil.\",\n             \"Mix all remaining ingredients except the chicken in the baking dish and stir together.\",\n                \"Wash the chicken and place in the baking dish, rolling the chicken to coat it with the mixture.\",\n                \"Cover the baking dish and cook for 45 minutes. Optional: uncover the baking dish the last 15 minutes.\",\n                \"Test the chicken for doneness and tenderness before serving.\"]},\n    {\"name\":\"Curried Chicken Legs\",\n        \"num_served\":8,\n        \"ingredients\":\n            [{\"name\":\"chicken legs\",\n                \"amount\":\"8\",\n                \"units\":\"\",\n                \"description\":\"Chicken legs\"},\n                {\"name\":\"curry powder\",\n                    \"amount\":\"4\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Curry powder\"},\n                {\"name\":\"fresh garlic (peeled and finely chopped)\",\n                    \"amount\":\"1\",\n                    \"units\":\"\",\n                    \"description\":\"Cloves garlic,crushed\"},\n                {\"name\":\"vegetable oil\", \"amount\":\"2\", \"units\":\"tablespoon\", \"description\":\"Oil\"},\n                {\"name\":\"yogurt\",\n                    \"amount\":\"1/2\",\n                    \"units\":\"cup\",\n                    \"description\":\"Plain yogurt\"},\n                {\"name\":\"lemon juice\",\n                    \"amount\":\"3\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Lemon juice\"},\n                {\"name\":\"brown sugar\",\n                    \"amount\":\"2\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Packed brown sugar\"}],\n        \"directions\":\n            [\"Preheat oven to 400F.\",\n                \"Cut at joint into thighs and  drumsticks. You can also but just thighs or the drumbsticks as to your prefenerence, adjusted the recipe.\",\n                \"Trim any hanging fat and excess skin from chicken.\",\n                \"Heat half of the oil in a small non-stick pan and slowly add in the curry powder, mixing well. Then immediately add the peeled and finely chopped garlic. Cook over medium heat for a few minutes until the garlic softens.\",\n                \"Put the contents of the pan into a large bowl, then mixing in yogurt, lemon juice and brownsugar.\",\n                \"Add chicken, rolling it around to thoroughly coat. Let this then sit at room temperature for at least 10 minutes.\",\n                \"Line a baking sheet with aluminum foil (always with the shiny side up) and use the remaining half of the oil to greese the foil.\",\n                \"Put the chicken on the sheet skin side down and cover the chcken with the remaining sauce from the large bowl.\",\n                \"Bake for  about 40 minutes. Check chicken for doneness before serving with either rice or pita bread.\",\n                \"Optional: serve with Chutney sauce, yogurt, and/or sour cream.\",\n                \"Pro tip: make enough for leftovers for lunch the next day!\"]},\n    {\"name\":\"Poached Salmon with Wine and Lemon\",\n        \"num_served\":4,\n        \"ingredients\":\n            [{\"name\":\"lemon juice\",\n                \"amount\":\"1/4\",\n                \"units\":\"cup\",\n                \"description\":\"Lemon juice\"},\n                {\"name\":\"white wine\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"cup\",\n                    \"description\":\"Dry white wine\"},\n                {\"name\":\"salmon (small fillets or steaks)\",\n                    \"amount\":\"4\",\n                    \"units\":\"\",\n                    \"description\":\"Salmon fillets\"},\n                {\"name\":\"salt\", \"amount\":\"1/4\", \"units\":\"teaspoon\", \"description\":\"Salt\"},\n                {\"name\":\"black pepper\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Pepper\"},\n                {\n                    \"name\": \"water\",\n                    \"amount\": \"1/4\",\n                    \"units\": \"cup\",\n                    \"description\": \"Water\"\n                }\n            ],\n        \"directions\":\n            [\"Use a heavy frying pan with a cover or small dutch oven to make this.\",\n                \"Over medium heat bring the lemon juice and wine to a boil, adding in the salt and pepper, then water.\",\n                \"Add salmon 'bad looking side' down and cover the pan.\",\n                \"Poach salmon for 7 or 8 minutes.  The fish should easily flake when it is done. Remove from the pan and eat immediately.\"]},\n    {\"name\":\"Honey-Curried Chicken\",\n        \"num_served\":6,\n        \"ingredients\":\n            [{\"name\":\"chicken breast\",\n                \"amount\":\"2\",\n                \"units\":\"pound\",\n                \"description\":\"Chicken breasts\"},\n                {\"name\":\"vegetable oil\",\n                    \"amount\":\"2\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Vegetable oil\"},\n                {\"name\":\"orange juice\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"cup\",\n                    \"description\":\"Orange juice\"},\n                {\"name\":\"honey\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"cup\",\n                    \"description\":\"Honey\"},\n                {\"name\":\"dijon mustard\",\n                    \"amount\":\"1\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Dijon mustard\"},\n                {\"name\":\"curry powder\",\n                    \"amount\":\"2\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Curry powder pinch cayenne pepper\"},\n                {\"name\":\"cayenne pepper\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"cayenne pepper\"}],\n        \"directions\":\n            [\"preheat oven to 400F\",\n                \"Grease a baking dish vegetable oil. Remove skin from chicken.\",\n                \"Arrange chicken in a  single layer bone side down if you are not using boneless chicken breasts.\",\n                \"Mix the remaining ingredients in a small bowl and pour 2/3 over the chicken.\",\n                \"Bake chicken covered for 10 minutes (15 minutes if bones in chicken breasts), basting once. Turn chicken over and baste with the remaining sauce.\",\n                \"Bake, now uncovered, for 15 minutes (20 minutes if bones in chicken breasts) longer. Test chicken for doneness and tenderness before serving.\"]},\n    {\"name\":\"Spicy Barbecued Chicken\",\n        \"num_served\":4,\n        \"ingredients\":\n            [{\"name\":\"chicken (whole fryer)\",\n                \"amount\":\"1\",\n                \"units\":\"\",\n                \"description\":\"chicken (2 lb)\"},\n                {\"name\":\"lemon\",\n                    \"amount\":\"1\",\n                    \"units\":\"\",\n                    \"description\":\"Juice of 1 lemon\"},\n                {\"name\":\"fresh garlic\",\n                    \"amount\":\"2\",\n                    \"units\":\"\",\n                    \"description\":\"Cloves garlic\"},\n                {\"name\":\"black pepper\",\n                    \"amount\":\"2\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Fresh ground black pepper\"},\n                {\"name\":\"salt\", \"amount\":\"1\", \"units\":\"teaspoon\", \"description\":\"Salt\"},\n                {\"name\":\"cayenne pepper\",\n                    \"amount\":\"2\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Ground cayenne pepper\"},\n                {\"name\":\"paprika\",\n                    \"amount\":\"1\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Paprika\"},\n                {\"name\":\"butter (melted)\",\n                    \"amount\":\"2\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Melted butter\"}],\n        \"directions\":\n            [\"Cut as much extra fat and loose skin as you can from the chicken. Later when you are grilling it this will help to minimize grease flames burning the chicken.\",\n                \"Split the chicken by cutting down the backbone and opening it to leave the breast attached - flatten out as much as possible while still keeping the chicken in one piece.\",\n                \"Squeeze the juice from the lemon on to the  chicken. Mix together the cayenne pepper, black pepper, salt, peeled and crushed garlic, paprika and add to the melted butter; pur and rub this mixture over the chicken.\",\n                \"Refrigerate the chicken (it can be uncovered) for at least an hour or two to let the spices set.\",\n                \"Prepare a gas grill or charcoal grill. For a gas grill heat it to medium temperature. For a charcoal grill, use a modest amount of charcoal to avoid too high of cooking heat. Put the  chicken breast side up onto the grill and cover the grill with the lid.\",\n                \"Cook chicken for 30 minutes without turning it. Turn the chicken and cook uncovered for about another 5 to 10 minutes to crisp up the skin.\",\n                \"Remove the chicken from the grill, cut into a thick area (thigh or breast) to make sure it is done, let it cool for 5 minutes and then serve.\",\n                \"Note: if you substitute chicken parts like legs or breasts then the cooking time will be reduced; keep an eye on the chicken and test for doneness before serving.\"]},\n    {\"name\":\"Indonesian Chicken Sate\",\n        \"num_served\":4,\n        \"ingredients\":\n            [{\"name\":\"chicken breast (half breasts, bones and skin removed)\",\n                \"amount\":\"4\",\n                \"units\":\"\",\n                \"description\":\n                    \"Skinless, boneless chicken breast halves (about 1 1/2 pounds)\"},\n                {\"name\":\"soy sauce\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"cup\",\n                    \"description\":\"Reduced-sodium soy sauce\"},\n                {\"name\":\"lemon\",\n                    \"amount\":\"1\",\n                    \"units\":\"\",\n                    \"description\":\"Fresh lemon juice\"},\n                {\"name\":\"molasses\",\n                    \"amount\":\"1\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Dark molasses\"},\n                {\"name\":\"red pepper flakes\",\n                    \"amount\":\"1\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Red Pepper Flakes\"},\n                {\"name\":\"fresh garlic (peeled and very finely chopped)\",\n                    \"amount\":\"1\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Garlic Powder Granules\"},\n                {\"name\":\"fresh ginger\",\n                    \"amount\":\"1\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Ginger\"}],\n        \"directions\":\n            [\"Cut breasts into long 1 inch strips. Put the chicken and all of the other ingredients into a large bowl and mix well making sure the chicken is well coated. Cover the bowl and refrigerate for two hours or longer. Stir up the bowl a few times while the chicken is marinating.\",\n                \"You can use either a (gas or charcoal) grill or the broiler in your oven to cook the Sate.\",\n                \"Optional: skewer the chicken strips on wet bamboo skewers - or you can cook the chicken loose if you don't have skewers.\",\n                \"Broil or grill  about 4 minutes on each side, occasionally applying more marinade.\",\n                \"Optional: if you have any available, serve with Indonesian or Thai peanut sauce, or chutney.\"]}\n\n\n\n\n\n\n\n\n\n\n]"
  },
  {
    "path": "cooking_recipes/data/misc.json",
    "content": "[\n    {\n        \"name\": \"Whole Wheat Bread\",\n        \"num_served\":10,\n        \"ingredients\": [\n            {\n                \"name\": \"water\",\n                \"amount\": \"1 1/2\",\n                \"units\": \"cup\",\n                \"description\": \"Water\"\n            },\n            {\n                \"name\": \"vegetable oil\",\n                \"amount\": \"1 1/3\",\n                \"units\": \"tablespoon\",\n                \"description\": \"Vegetable oil\"\n            },\n            {\n                \"name\": \"honey\",\n                \"amount\": \"1/4\",\n                \"units\": \"cup\",\n                \"description\": \"Honey\"\n            },\n            {\n                \"name\": \"salt\",\n                \"amount\": \"1/2\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Salt\"\n            },\n            {\n                \"name\": \"whole wheat flour\",\n                \"amount\": \"4\",\n                \"units\": \"cup\",\n                \"description\": \"Whole wheat bread flour\"\n            },\n            {\n                \"name\": \"yeast\",\n                \"amount\": \"3\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Yeast\"\n            }\n        ],\n        \"directions\": [\n            \"Lightly grease two 9x5 inch loaf pans.\",\n            \"Blend all ingredients and then place on a large floured surface and knead for 5 or 6 minutes.\",\n            \"Place in a covered bowl in a warm area and let it rise for about one hour.\",\n            \"Preheat oven to 375F. Bake for 35 to 40 minutes.\"]\n    },\n    {\n        \"name\": \"Homemade Salad Dressing\",\n        \"num_served\":16,\n        \"ingredients\": [\n            {\n                \"name\": \"salt\",\n                \"amount\": \"1\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Salt\"\n            },\n            {\n                \"name\": \"black pepper\",\n                \"amount\": \"1/4\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Black Pepper\"\n            },\n            {\n                \"name\": \"paprika\",\n                \"amount\": \"1/2\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Paprika\"\n            },\n            {\n                \"name\": \"dry mustard\",\n                \"amount\": \"1\",\n                \"units\": \"pinch\",\n                \"description\": \"Dry Mustard\"\n            },\n            {\n                \"name\": \"brown sugar\",\n                \"amount\": \"1\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Sugar\"\n            },\n            {\n                \"name\": \"olive oil\",\n                \"amount\": \"1/2\",\n                \"units\": \"cup\",\n                \"description\": \"Olive Oil\"\n            },\n            {\n                \"name\": \"red wine vinegar\",\n                \"amount\": \"1/4\",\n                \"units\": \"cup\",\n                \"description\": \"Wine Vinegar\"\n            },\n            {\n                \"name\": \"fresh garlic \",\n                \"amount\": \"2\",\n                \"units\": \"clove\",\n                \"description\": \"Clove Garlic\"\n            }\n        ],\n        \"directions\": [\n            \"Dissolve sugar, salt, pepper, paprika, and dry mustard in olive oil.\",\n            \"Stir in vinegar. Peel and mince garlic and and add to dressing.\"]\n    },\n    {\n        \"name\": \"Aioli Sauce\",\n        \"num_served\":8,\n        \"ingredients\": [\n            {\n                \"name\": \"egg yolks\",\n                \"amount\": \"3\",\n                \"units\": \"unit\",\n                \"description\": \"Egg yolks\"\n            },\n            {\n                \"name\": \"fresh garlic\",\n                \"amount\": \"4\",\n                \"units\": \"clove\",\n                \"description\": \"Cloves garlic\"\n            },\n            {\n                \"name\": \"lemon juice\",\n                \"amount\": \"1\",\n                \"units\": \"tablespoon\",\n                \"description\": \"Lemon juice\"\n            },\n            {\n                \"name\": \"salt\",\n                \"amount\": \"1/3\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Salt\"\n            },\n            {\n                \"name\": \"tabasco sauce\",\n                \"amount\": \"1/4\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Tabasco\"\n            },\n            {\n                \"name\": \"worcestershire sauce\",\n                \"amount\": \"1/4\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Worcestershire sauce\"\n            },\n            {\n                \"name\": \"black pepper\",\n                \"amount\": \"1/4\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Freshly ground black pepper\"\n            },\n            {\n                \"name\": \"mayonnaise\",\n                \"amount\": \"1/4\",\n                \"units\": \"cup\",\n                \"description\": \"Mayonnaise\"\n            },\n            {\n                \"name\": \"olive oil\",\n                \"amount\": \"3/4\",\n                \"units\": \"cup\",\n                \"description\": \"Extra virgin olive oil\"\n            },\n            {\n                \"name\": \"water (boiling)\",\n                \"amount\": \"3\",\n                \"units\": \"tablespoon\",\n                \"description\": \"Boiling water\"\n            }\n        ],\n        \"pictures\":\n          [\n            [\"aioli_sauce\", \"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Prepared Aioli Sauce<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(photo by Ralph Sperling public domain)\"]\n          ],\n        \"directions\": [\"Blend together the egg yolks, garlic, lemon, salt and pepper in a blender or food processor for about 2 minutes.\",\n            \"Very slowly add the oil and mayonnaise while continuing to blend the mixture.\",\n            \"Add the seasonings and mix together. Add the boiling water, stir well, and refrigerate for later use.\"]\n    },\n    {\n        \"name\": \"Indonesian Barbecue Sauce\",\n        \"num_served\":12,\n        \"ingredients\": [\n            {\n                \"name\": \"corn syrup\",\n                \"amount\": \"1/2\",\n                \"units\": \"cup\",\n                \"description\": \"Corn syrup (dark)\"\n            },\n            {\n                \"name\": \"peanut butter\",\n                \"amount\": \"1/4\",\n                \"units\": \"cup\",\n                \"description\": \"Creamy peanut butter\"\n            },\n            {\n                \"name\": \"soy sauce\",\n                \"amount\": \"1/4\",\n                \"units\": \"cup\",\n                \"description\": \"Soy sauce\"\n            },\n            {\n                \"name\": \"vinegar\",\n                \"amount\": \"1/4\",\n                \"units\": \"cup\",\n                \"description\": \"Cider vinegar\"\n            },\n            {\n                \"name\": \"green onions\",\n                \"amount\": \"1/4\",\n                \"units\": \"cup\",\n                \"description\": \"Sliced green onions\"\n            },\n            {\n                \"name\": \"fresh garlic (peeled and minced)\",\n                \"amount\": \"4\",\n                \"units\": \"clove\",\n                \"description\": \"garlic\"\n            },\n            {\n                \"name\": \"cloves (ground)\",\n                \"amount\": \"1\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Clove \"\n            },\n            {\n                \"name\": \"fresh ginger (peeled and minced)\",\n                \"amount\": \"2\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Ginger\"\n            },\n            {\n                \"name\": \"red pepper flakes\",\n                \"amount\": \"1\",\n                \"units\": \"teaspoon\",\n                \"description\": \"Crushed dried red pepper\"\n            }\n        ],\n        \"directions\": [\"Mix together all ingredients and let sit for at least one hour before using.\",\n            \"Refrigerate or freeze any unused barbecue sauce for future use.\"]\n    },\n    {\"name\":\"Basil Vinaigrette Salad Dressing\",\n        \"num_served\":12,\n        \"ingredients\":\n            [{\"name\":\"balsamic vinegar\",\n                \"amount\":\"1/4\",\n                \"units\":\"cup\",\n                \"description\":\"balsamic vinegar\"},\n                {\"name\":\"dried basil\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"dried basil (or 1/2 tsp fresh)\"},\n                {\"name\":\"dried oregano\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Dried oregano (or 1/2 tsp fresh)\"},\n                {\n                    \"name\": \"olive oil\",\n                    \"amount\": \"1\",\n                    \"units\": \"tablespoon\",\n                    \"description\": \"Olive Oil\"\n                }\n            ],\n        \"directions\":\n            [\"Mix all ingredients in a bowl. Let sit at least 2 or 3 minutes before using on a salad.\",\n            \"Refrigerate extra dressing for future use.\"]},\n    {\"name\":\"Black Bean Dip\",\n        \"num_served\":6,\n        \"ingredients\":\n            [{\"name\":\"black beans (cooked or canned)\",\n                \"amount\":\"2\",\n                \"units\":\"cup\",\n                \"description\":\"Refried Black Beans (see rec.)\"},\n                {\"name\":\"sour cream\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"cup\",\n                    \"description\":\"Sour cream\"},\n                {\"name\":\"cumin\",\n                    \"amount\":\"1\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Ground cumin\"},\n                {\"name\":\"salsa\",\n                    \"amount\":\"1/2\",\n                    \"units\":\"cup\",\n                    \"description\":\"Salsa\"}],\n        \"directions\":\n            [\"Use either a food processor or a mixing bowl and hand mixer to make this appetizer.\",\n                \"Blend the black beans and cumin for at least one minute until the mixture is fairly smooth.\",\n                \"Stir in salsa and sour cream and lightly mix. Serve immediately or store in the refrigerator.\"]},\n    {\"name\":\"Borscht\",\n        \"num_served\":4,\n        \"ingredients\":\n            [{\"name\":\"beets (raw and sliced)\", \"amount\":\"2\", \"units\":\"cup\", \"description\":\"Beets\"},\n                {\"name\":\"brown onions\",\n                    \"amount\":\"1\",\n                    \"units\":\"\",\n                    \"description\":\"Onion\"},\n                {\"name\":\"fresh garlic\",\n                    \"amount\":\"2\",\n                    \"units\":\"clove\",\n                    \"description\":\"Garlic clove\"},\n                {\"name\":\"water\", \"amount\":\"2\", \"units\":\"quart\", \"description\":\"Water\"},\n                {\"name\":\"salt\", \"amount\":\"2\", \"units\":\"teaspoon\", \"description\":\"Salt\"},\n                {\"name\":\"brown sugar\",\n                    \"amount\":\"3\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Sugar\"},\n                {\"name\":\"lemon juice\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"cup\",\n                    \"description\":\"Lemon juice\"}],\n        \"directions\":\n            [\"Wash and peel beets and thickly slice so they cook faster. Peel the onion and garlic cloves. Leave the onion and garlic cloves whole since we will be removing them later.\",\n                \"Mix everything except the lemon juice and sugar in a large covered pot and cook over low heat for one hour.\",\n                \"Add the sugar and lemon juice.  Cook an additional thirty minutes, remove from heat and let cool. Discard the onion and garlic and use a hand mixer to blend into a cold soup.\",\n                \"Optional: place a small spoonful of sour cream on top of each serving.\"]},\n    {\"name\":\"Curried Yogurt Dip\",\n        \"num_served\":6,\n        \"ingredients\":\n            [{\"name\":\"yogurt (plain)\",\n                \"amount\":\"1\",\n                \"units\":\"cup\",\n                \"description\":\"Plain Yogurt\"},\n                {\"name\":\"curry powder\",\n                    \"amount\":\"1.5\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Curry powder\"},\n                {\"name\":\"sriracha hot chili sauce\",\n                    \"amount\":\"1/2\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Hot pepper sauce\"},\n                {\"name\":\"lemon juice\",\n                    \"amount\":\"1\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Lemon juice\"},\n                {\"name\":\"honey\",\n                    \"amount\":\"1/2\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Honey\"},\n                {\"name\":\"black pepper\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Black pepper\"},\n                {\"name\":\"salt\", \"amount\":\"1/4\", \"units\":\"teaspoon\", \"description\":\"Salt\"},\n                {\"name\":\"almonds\", \"amount\":\"3\", \"units\":\"tablespoon\", \"description\":\"almonds\"}],\n        \"directions\":\n            [\"Combine ingredients (except almonds!) stirring well.\",\n             \"Divide into four small serving bowls. Set aside for about 15 minutes at room temperature before serving.\",\n                \"Then, finely chop almonds are sprinkle on top of the yogurt dip.\",\n                \"Place the small bowls containing the dip on larger plates with cut up fresh vegetables and/or chips.\"]},\n    {\"name\":\"Dijon Sour Cream Dip\",\n        \"num_served\":6,\n        \"ingredients\":\n            [{\"name\":\"sour cream\",\n                \"amount\":\"1\",\n                \"units\":\"cup\",\n                \"description\":\"Sour cream OR Plain yogurt\"},\n                {\"name\":\"mayonnaise\",\n                    \"amount\":\"1/3\",\n                    \"units\":\"cup\",\n                    \"description\":\"Mayonnaise\"},\n                {\"name\":\"dijon mustard\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"cup\",\n                    \"description\":\"Dijon mustard\"},\n                {\"name\":\"green onions\",\n                    \"amount\":\"1\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Finely-chopped green onion\"},\n                {\"name\":\"salt\",\n                    \"amount\":\"0.25\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Seasoned salt\"},\n                {\"name\":\"black pepper\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Black pepper\"}],\n        \"directions\":\n            [\"Mix all ingredients, cover and store in the refrigerator for at least 30 minutes before serving.\",\n                \"Serve dip into small bowls containing and place on larger plates with cut up fresh vegetables and/or chips.\"]},\n    {\"name\":\"Dijon Yogurt Dip\",\n        \"num_served\":6,\n        \"ingredients\":\n            [{\"name\":\"yogurt\",\n                \"amount\":\"1\",\n                \"units\":\"cup\",\n                \"description\":\"Sour cream OR Plain yogurt\"},\n                {\"name\":\"mayonnaise\",\n                    \"amount\":\"1/3\",\n                    \"units\":\"cup\",\n                    \"description\":\"Mayonnaise\"},\n                {\"name\":\"dijon mustard\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"cup\",\n                    \"description\":\"Dijon mustard\"},\n                {\"name\":\"green onions\",\n                    \"amount\":\"2\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Finely-chopped green onion\"},\n                {\"name\":\"salt\",\n                    \"amount\":\"0.5\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Seasoned salt\"},\n                {\"name\":\"black pepper\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Black pepper\"}],\n        \"directions\":\n            [\"Mix all ingredients, cover and store in the refrigerator for at least 30 minutes before serving.\",\n                \"Serve dip into small bowls containing and place on larger plates with cut up fresh vegetables and/or chips.\"]},\n    {\"name\":\"English Scones\",\n        \"num_served\":6,\n        \"ingredients\":\n            [{\"name\":\"flour\", \"amount\":\"2.5\", \"units\":\"cup\", \"description\":\"Flour\"},\n                {\"name\":\"cream of tartar\",\n                    \"amount\":\"1\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Cream of tartar\"},\n                {\"name\":\"baking soda\",\n                    \"amount\":\"1\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Baking soda\"},\n                {\"name\":\"brown sugar\",\n                    \"amount\":\"3\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Sugar\"},\n                {\"name\":\"salt\", \"amount\":\"1/2\", \"units\":\"teaspoon\", \"description\":\n                    \"Salt\"},\n                {\"name\":\"butter\",\n                    \"amount\":\"1/2\",\n                    \"units\":\"cup\",\n                    \"description\":\"Butter\"},\n                {\"name\":\"milk\",\n                    \"amount\":\"1\",\n                    \"units\":\"cup\",\n                    \"description\":\"Milk\"}],\n        \"directions\":\n            [\"Preheat oven to 400F.\",\n                \"Mix flour, cream of tartar, baking soda, sugar, and salt together. Slowly mix in the butter and when all of the butter is mixed in the mix in the milk.\",\n                \"Form into three round balls on  floured board and then flatten. Partially cut through each of the three portions to make four pie-like portions.\",\n                \"Bake for 15 minutes. Cool for a few minutes then serve immediateley.\"]},\n    {\"name\":\"Tasty Black Bean Dip\",\n        \"num_served\":4,\n        \"ingredients\":\n            [{\"name\":\"black beans\",\n                \"amount\":\"1.5\",\n                \"units\":\"cup\",\n                \"description\":\"16-0z can black beans\"},\n                {\"name\":\"sweet red onions (finely chopped)\",\n                    \"amount\":\"2\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Chopped red onion\"},\n                {\"name\":\"balsamic vinegar\",\n                    \"amount\":\"1\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Balsamic vinegar\"},\n                {\"name\":\"fresh garlic (peeled and finely chopped)\",\n                    \"amount\":\"1\",\n                    \"units\":\"\",\n                    \"description\":\"Clove garlic\"},\n                {\n                    \"name\": \"olive oil\",\n                    \"amount\": \"1/2\",\n                    \"units\": \"cup\",\n                    \"description\": \"Olive Oil\"\n                }\n            ],\n        \"directions\":\n            [\"You can use canned black beans (each 12 ounce can is about 1 1/2 cups) or cook dry beans according to package directions and then cool.\",\n             \"Combine ingredients in the blender or food processor and mix for about 1 minute to either a fine or course consistency depending on your tastes and preferences.\",\n             \"I like to serve this in small individual bowls place on a larger plate so there is plenty of room on each plate for pita bread, chips, and/or cut up raw vegetables.\"]},\n    {\"name\":\"Fresh Tomato Sauce\",\n        \"num_served\":4,\n        \"ingredients\":\n            [{\"name\":\"tomatoes (medium size, diced)\",\n                \"amount\":\"12\",\n                \"units\":\"\",\n                \"description\":\"Tomatoes\"},\n                {\"name\":\"brown sugar\",\n                    \"amount\":\"2\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Sugar\"},\n                {\"name\":\"black pepper\",\n                    \"amount\":\"1\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"black pepper\"},\n                {\"name\":\"salt\", \"amount\":\"1\", \"units\":\"teaspoon\", \"description\":\"Salt\"},\n                {\"name\":\"fresh basil\",\n                    \"amount\":\"1\",\n                    \"units\":\"cup\",\n                    \"description\":\"Fresh basil\"}],\n        \"directions\":\n            [\"Place all  ingredients in heavy saucepan or dutch oven and bring to a slow boil. Lower heat  and cook covered for 40 minutes, stirring occasionally.\",\n             \"I suggest using a small amount for a meal immediately (with cooked pasta) and freezing most of it for future meals.\",\n             \"This is a great recipe to use up extra tomatoes from your garden or when tomatoes are in season and inexpensive to buy.\"]},\n    {\"name\":\"Japanese Ginger Salad Dressing\",\n        \"num_served\":4,\n        \"ingredients\":\n            [{\"name\":\"rice vinegar\",\n                \"amount\":\"1\",\n                \"units\":\"tablespoon\",\n                \"description\":\"Rice vinegar\"},\n                {\"name\":\"vegetable oil\",\n                    \"amount\":\"2\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Vegetable oil\"},\n                {\"name\":\"sesame oil\",\n                    \"amount\":\"1.5\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Sesame oil\"},\n                {\"name\":\"fresh ginger (grated or very finely chopped)\",\n                    \"amount\":\"1\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Grated fresh ginger\"},\n                {\"name\":\"soy sauce\",\n                    \"amount\":\"1\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Soy sauce\"}],\n        \"directions\":[\"Mix all ingredients together and let sit for at least 10 minutes before using.\",\n        \"Some of the ingredients will settle so make sure you stir well before pouring over salad.\"]},\n    {\"name\":\"Quick Sweet and Sour Sauce\",\n        \"num_served\":8,\n        \"ingredients\":\n            [{\"name\":\"white vinegar\",\n                \"amount\":\"1\",\n                \"units\":\"cup\",\n                \"description\":\"White vinegar\"},\n                {\"name\":\"brown sugar\",\n                    \"amount\":\"2/3\",\n                    \"units\":\"cup\",\n                    \"description\":\"Sugar\"},\n                {\"name\":\"ketchup\",\n                    \"amount\":\"2\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"Ketchup\"},\n                {\"name\":\"fresh ginger (peeled and very finely chopped)\",\n                    \"amount\":\"2\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"fresh ginger\"},\n                {\"name\":\"salt\", \"amount\":\"1/4\", \"units\":\"teaspoon\", \"description\":\n                    \"Salt\"},\n                {\"name\":\"tabasco sauce\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Tabasco sauce\"}],\n        \"directions\":\n            [\"Use a medium size pan to combine the vinegar, sugar, ketchup, ginger, salt and tabasco sauce and bring to a boil under high heat.\",\n             \"Cook until the sauce is reduced to a fairly thick sauce. You don't want the sauce to be runny but you also don't want it to be too thick. Pay attention while cooking the sauce!\"]}\n\n\n\n\n\n\n\n\n\n\n]"
  },
  {
    "path": "cooking_recipes/data/recipes.json",
    "content": "[\n    {\n        \"items\": [\n            [\"onions\", [\"2 \", \"onion\"], 2.0, \"onion\"],\n            [\"fresh garlic (peeled and finely sliced)\", [\"1 1/2\", \"tablespoons\"], 0.09, \"cup\"],\n            [\"green pepper\", [\"1 \", \"cup chopped\"], 1.0, \"cup chopped\"],\n            [\"tomatoes\", [\"2 \", \"cup chopped\"], 2.0, \"cup chopped\"],\n            [\"salt\", [\"2 \", \"teaspoons\"], 0.04, \"cup\"],\n            [\"black pepper\", [\"1/3\", \"teaspoons\"], 0.125, \"tbsp\"],\n            [\"Worcestershire Sauce\", [\"1 1/5\", \"tablespoons\"], 0.075, \"cup\"],\n            [\"paprika\", [\"2 \", \"teaspoons\"], 0.6667, \"tbsp\"],\n            [\"red wine vinegar\", [\"1/3\", \"cup\"], 0.33333, \"cup\"],\n            [\"water\", [\"12 \", \"fl oz\"], 12.0, \"fl oz\"],\n            [\"cucumber\", [\"1 \", \"cup pared chopped\"], 1.0, \"cup pared chopped\"],\n            [\"chives\", [\"1 \", \"tbsp chopped\"], 1.0, \"tbsp chopped\"],\n            [\"olive oil\", [\"5 \", \"tablespoons\"], 5.0, \"tbsp\"]\n        ],\n        \"recipe\": {\n            \"rating\": 3.0,\n            \"name\": \"Gazpacho\",\n            \"directions\": \"Combine the onions, garlic, green peppers and tomatoes. Force through a sieve or puree in a blender. Add the salt, pepper and paprika. Add the olive oil gradually, beating steadily. Add the vinegar and water and stir well. Season to taste. Refrigerate and chill for at least two hours, using a wooden or glass bowl; do not use a metal bowl. Add the cucumber slices before serving.\\u003Cbr/\\u003E\\u003Cbr/\\u003EIf desired, slices of toast rubbed with garlic may be served with the Gazpacho. \",\n            \"num_served\": 4,\n            \"id\": 1,\n            \"num_ratings\": 2\n        }\n    },\n    {\n        \"items\": [\n            [\"chicken breast\", [\"1 \", \"unit ( yield from 1 lb ready-to-cook chicken)\"], 1.0, \"unit ( yield from 1 lb ready-to-cook chicken)\"],\n            [\"olive oil\", [\"1 \", \"tablespoons\"], 1.0, \"tbsp\"],\n            [\"curry powder\", [\"2 1/4\", \"teaspoons\"], 0.75, \"tbsp\"],\n            [\"cumin seeds\", [\"2/3\", \"tbsp whole\"], 0.6667, \"tbsp whole\"],\n            [\"yogurt (low fat plain)\", [\"3/4\", \"cup ( 8 fl oz)\"], 0.75, \"cup ( 8 fl oz)\"],\n            [\"fresh peaches\", [\"1 1/2\", \"cup slices\"], 1.5, \"cup slices\"],\n            [\"apples (tart with skin)\", [\"1 \", \"cup slices\"], 1.0, \"cup slices\"],\n            [\"celery\", [\"1/2\", \"cup chopped\"], 0.5, \"cup chopped\"],\n            [\"cashew (nuts)\", [\"1/2 \", \"cup\"], 0.5, \"cup\"],\n            [\"black pepper\", [\"1/5\", \"tbsp ground\"], 0.2, \"tbsp ground\"]\n        ],\n        \"recipe\": {\n            \"rating\": 3.0,\n            \"name\": \"Curried Chicken Salad\",\n            \"directions\": \"Toast cumin seeds for one or two minutes until they become fragrant and slightly browned. Peel and slice peaches. Bone and skin chicken breasts. Thinly slice celery and green onions. Dice unpeeled tart apples.\\u003Cbr/\\u003E\\u003Cbr/\\u003EMix oil, one tsp. of curry powder and one tsp. of cumin. Coat chicken breasts and broil until done. Set aside.\\u003Cbr/\\u003E\\u003Cbr/\\u003EIn a medium bowl add peaches, celery, green onions, yogurt, and remaining curry and cumin. Add cut up chicken and mix well.\\u003Cbr/\\u003E\\u003Cbr/\\u003EGarnish with peaches and cut up chives.\",\n            \"num_served\": 4,\n            \"id\": 2,\n            \"num_ratings\": 2\n        }\n    },\n    {\n        \"items\": [\n            [\"Cornish Game Hens\", [\"4 \", \"bird whole\"], 4.0, \"bird whole\"],\n            [\"black pepper\", [\"1 1/2\", \"teaspoons\"], 0.5, \"tbsp\"],\n            [\"dried basil\", [\"2 \", \"tsp leaves\"], 2.0, \"tsp leaves\"],\n            [\"rosemary\", [\"1 \", \"tsp leaves\"], 1.0, \"tsp leaves\"],\n            [\"salt\", [\"1 1/2\", \"teaspoons\"], 0.03, \"cup\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Barbecued Cornish Game Hens\",\n            \"directions\": \"Wash the Cornish Game Hens and with a large knife cut them each in two pieces (each piece has one leg, one wing, half a breast, and half a back). Trim the excess skin and fat from the cut edges, wash the hens in cold water and place in a large covered baking dish. Add the salt, pepper, and dried basil to the baking dish and rub the ingredients over the hens. Wash your hands with plenty of soap (because you just handled raw poultry), cover the baking dish, and cook in a 350 degree oven for about 30 minutes.\\u003Cbr/\\u003E\\u003Cbr/\\u003EStart the coals on your barbecue. Take the hens from the oven baking dish and place back side down over the hot coals. Cook for about 10 minutes and then turn over and cook for an additional 10 minutes. It might be necessary to move the hens if the burning charcoal flares up (if you did cut away some excess skin and fat this is likely to not happen).\\u003Cbr/\\u003E\\u003Cbr/\\u003ECooking time may vary depending on how hot the coals are.\\u003Cbr/\\u003E\\u003Cbr/\\u003ECut into the hens with a small sharp knife to make sure that they are done before serving.\\u003Cbr/\\u003E\\u003Cbr/\\u003EOptional:we sometimes spread a little teriyaki or barbeque sauce on the hens while cooking on the barbeque. They do also taste great with just the basil, salt, and pepper.\\u003Cbr/\\u003E\\u003Cbr/\\u003EServing size:a whole Cornish Game Hen makes a large serving for one person. We usually serve a whole hen per person only if we are just having a salad with the hen. If you have a few side dishes, then half a Cornish Game Hen makes a good serving. \",\n            \"num_served\": 4,\n            \"id\": 3,\n            \"num_ratings\": 1\n        },\n        \"pictures\":\n         [\n           [\"barbequed_cornish_game_hen\", \"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Prepared Barbecued Cornish Game Hen<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(photo by Davie Boy public domain)\"]\n         ]\n    },\n    {\n        \"items\": [\n            [\"chicken (fryer)\", [\"1 \", \"unit ( yield from 1 lb ready-to-cook chicken)\"], 1.0, \"unit ( yield from 1 lb ready-to-cook chicken)\"],\n            [\"vegetable oil\", [\"1/3\", \"cup\"], 0.33333, \"cup\"],\n            [\"water\", [\"16 \", \"fl oz\"], 16.0, \"fl oz\"],\n            [\"brown onions (peeled and chopped)\", [\"1/2\", \"cup chopped\"], 0.5, \"cup chopped\"],\n            [\"green pepper (seeded and chopped in strips)\", [\"1/2\", \"cup strips\"], 0.5, \"cup strips\"],\n            [\"white rice\", [\"1 \", \"cup\"], 1.0, \"cup\"],\n            [\"tumeric\", [\"1/3\", \"teaspoons\"], 0.1, \"tbsp\"],\n            [\"chicken broth (canned)\", [\"1 \", \"cup\"], 1.0, \"cup\"],\n            [\"frozen peas\", [\"1/2\", \"(0.5) cup\"], 0.5, \"(0.5) cup\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Arroz con Pollo\",\n            \"directions\": \"Heat oil in a large skillet over moderately high heat. Add lightly salted chicken pieces and brown on all sides for about 15 minutes. Reduce heat to low. Add 1/3 cup of water and simmer for approximately 10 minutes. Remove chicken and juices from pan and set aside. Return 3 tablespoons of liquid to pan, add onion, rice and green pepper and cook over moderately high head stirring occasionally until mixture is lightly browned. Return de-fatted remaining pan juices to pan with two cups of water, tumeric, 1 tsp. Salt, pepper, broth mix and pimiento.\\u003Cbr/\\u003E\\u003Cbr/\\u003EBring rice to a boil, arrange browned chicken on top. Reduce heat to moderately low, cover and cook 10 minutes. Sprinkle peas over chicken, cover and continue cooking 5 to 10 minutes.\",\n            \"num_served\": 4,\n            \"id\": 5,\n            \"num_ratings\": 1\n        },\n        \"pictures\":\n          [\n             [\"arroz_con_pollo\", \"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Prepared Arroz con Pollo<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(photo by Kobako CC BY-SA 2.5)\"]\n          ]\n    },\n    {\n        \"items\": [\n            [\"salmon (fillet, skin removed)\", [\"1\", \"unit\"], 1, \"oz boneless\"],\n            [\"brown rice\", [\"1 \", \"cup\"], 1.0, \"cup\"],\n            [\"chicken broth (canned)\", [\"2 \", \"cup\"], 2.0, \"cup\"],\n            [\"pine nuts\", [\"1/3\", \"cup\"], 0.3333, \"cup\"],\n            [\"sesame oil\", [\"2 \", \"tablespoons\"], 2.0, \"tbsp\"],\n            [\"broccoli\", [\"2 \", \"cup chopped\"], 2.0, \"cup chopped\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Salmon Rice\",\n            \"directions\": \"In a heavy pot suitable for cooking rice, add the finely chopped onion and about one-third of the chicken broth. Cover and cook over low heat for a few minutes. Add half the sesame oil, the washed brown rice and the pine (pinyon) nuts. Add the water and bring to a boil. As soon as the water comes to a boil, turn down the heat as low as possible, cover tightly and cook for about 20 to 25 minutes or until the mixture dries out and the rice is tender.\\u003Cbr/\\u003E\\u003Cbr/\\u003EWash the broccoli, discarding only the very bottom of the stems. Separate the florets into bite size pieces. Note: The broccoli stems are very nutritious, but will need to be cooked slightly longer than the rest of the broccoli. Cut them off just just under the tops and slice thinly or dice into 1/8\\\" pieces.\\u003Cbr/\\u003E\\u003Cbr/\\u003EMix the prepared broccoli stems, the rest of the chicken broth, and the rest of the sesame oil in a heavy skillet. Cook covered over medium heat for about 5 or 6 minutes. Stir in the broccoli florets and cook for about one additional minute. Remove the skin and bones from the salmon and add to the pan. Cook covered for just under 10 minutes over low heat until done, being careful not to overcook.\\u003Cbr/\\u003E\\u003Cbr/\\u003EServe the rice on one side of each plate and add salmon mixture from the skillet on the other side. Add a bit of fresh parsley garnish, if available.\",\n            \"num_served\": 4,\n            \"id\": 6,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"black beans\", [\"1 \", \"cup\"], 1.0, \"cup\"],\n            [\"adzuki beans\", [\"1/2\", \"cup\"], 0.5, \"cup\"],\n            [\"kidney beans\", [\"1/2\", \"cup\"], 0.5, \"cup\"],\n            [\"pinto beans\", [\"1/2\", \"cup\"], 0.5, \"cup\"],\n            [\"fresh garlic\", [\"1 1/5\", \"tablespoons\"], 0.075, \"cup\"],\n            [\"dried oregano\", [\"2 \", \"tsp leaves\"], 2.0, \"tsp leaves\"],\n            [\"coriander\", [\"1/2 \", \"tsp leaves\"], 0.5, \"tsp leaves\"],\n            [\"marjoram\", [\"1/2 \", \"tsp\"], 0.5, \"tsp\"],\n            [\"cumin\", [\"1/2\", \"tsp\"], 0.5, \"tsp\"],\n            [\"cumin seeds\", [\"1/2\", \"tsp whole\"], 0.5, \"tsp whole\"],\n            [\"dried bay leaf\", [\"1/2\", \"tbsp crumbled\"], 0.5, \"tbsp crumbled\"],\n            [\"black pepper\", [\"1 \", \"tablespoons\"], 1.0, \"tbsp\"],\n            [\"brown onions (chopped)\", [\"1 \", \"cup chopped\"], 1.0, \"cup chopped\"],\n            [\"chili powder\", [\"1 1/2\", \"tablespoons\"], 1.5, \"tbsp\"],\n            [\"salt\", [\"1 \", \"teaspoons\"], 0.02, \"cup\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"SouthWest Beans\",\n            \"directions\": \"Inspect the beans making sure there are no small stones. Rinse.\\u003Cbr/\\u003E\\u003Cbr/\\u003EWash the beans and soak overnight (up to 24 hours is OK). If you do not have time to presoak the beans, boil for 5 minutes and turn off heat for about one hour.\\u003Cbr/\\u003E\\u003Cbr/\\u003EDrain and wash beans and then add to a large soup pot with 5 to 6 cups of water. (You might have to add additional water later). Add all ingredients (but no salt, this makes them tough during cooking), cover, and cook at a slow boil for a few hours, adding water as required to keep the beans slightly covered in water.\\u003Cbr/\\u003E\\u003Cbr/\\u003E The onion should be finely chopped. Cook covered for another hour. Note: if there is too much water in the beans, you can cook them uncovered until the liquid is reduced.\\u003Cbr/\\u003E\\u003Cbr/\\u003EAdd salt before serving. Adding salt earlier makes the beans tough. OPTIONAL: when the beans are cooked, put about 1/3 of the cooked beans in a blender, puree for about 10 seconds, and add them back to the rest of the cooked beans. This optional step gives the beans a tasty creamy consistency.\\u003Cbr/\\u003E\\u003Cbr/\\u003EIdea for left over beans: warm and serve with grated cheese and a fried egg on top.\",\n            \"num_served\": 4,\n            \"id\": 7,\n            \"num_ratings\": 1\n        },\n      \"pictures\":\n      [\n        [\"southwestbeans1\", \"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Beans soaking in water\"],\n        [\"southwestbeans2\", \"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;All ingredients starting to boil\"],\n        [\"southwestbeans3\", \"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;With puréed beens added back in\"],\n        [\"southwestbeans4\", \"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Save some for leftovers\"]\n      ]\n\n    },\n    {\n        \"items\": [\n            [\"T-Bone steak\", [\"1 \", \"piece cooked excluding refuse ( yield from 1 lb raw meat with refuse)\"], 1.0, \"piece cooked excluding refuse ( yield from 1 lb raw meat with refuse)\"],\n            [\"barbecue sauce\", [\"1 \", \"serving 2 tbsp\"], 1.0, \"serving 2 tbsp\"]\n        ],\n        \"recipe\": {\n            \"rating\": 3.0,\n            \"name\": \"Barbecued T-Bone Steak\",\n            \"directions\": \"Trim excess loose fat from steak.\\u003Cbr/\\u003E\\u003Cbr/\\u003EBarbecue over hot coals and add barbecue sauce to taste.\\u003Cbr/\\u003E\\u003Cbr/\\u003ERemember to wash your hands after handling raw meat!\",\n            \"num_served\": 1,\n            \"id\": 8,\n            \"num_ratings\": 2\n        }\n    },\n    {\n        \"items\": [\n            [\"egg\", [\"2 \", \"large\"], 2.0, \"large\"],\n            [\"mayonnaise\", [\"1 2/3\", \"tablespoons\"], 0.1, \"cup\"],\n            [\"iceberg lettuce\", [\"1/4\", \"cup shredded\"], 0.25, \"cup shredded\"],\n            [\"bread (toasted)\", [\"2 \", \"slice small\"], 2.0, \"slice small\"],\n            [\"bread (toasted sourdough french bread)\", [\"1/10\", \"teaspoons\"], 0.0007, \"cup\"],\n            [\"black pepper\", [\"1/3\", \"teaspoons\"], 0.1, \"tbsp\"]\n        ],\n        \"recipe\": {\n            \"rating\": 3.0,\n            \"name\": \"Egg Salad Sandwich\",\n            \"directions\": \"Peel boiled eggs, mash, and mix with mayonnaise.\\u003Cbr/\\u003E\\u003Cbr/\\u003EAdd salt and pepper to taste.\\u003Cbr/\\u003E\\u003Cbr/\\u003EServe with shredded lettuce on toast.\",\n            \"num_served\": 1,\n            \"id\": 10,\n            \"num_ratings\": 2\n        }\n    },\n    {\n        \"items\": [\n            [\"bread (toasted sourdough french)\", [\"1 \", \"slice small\"], 1.0, \"slice small\"],\n            [\"peanut butter\", [\"2 \", \"tablespoons\"], 2.0, \"tbsp\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Peanut butter toast\",\n            \"directions\": \"\",\n            \"num_served\": 1,\n            \"id\": 11,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"apples\", [\"2/3\", \"cup quartered or chopped\"], 0.6666, \"cup quartered or chopped\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Apple\",\n            \"directions\": \"Eat as is.\",\n            \"num_served\": 1,\n            \"id\": 12,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"apples\", [\"2/3\", \"cup quartered or chopped\"], 0.66666, \"cup quartered or chopped\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Fresh apple desert\",\n            \"directions\": \"Eat as is.\",\n            \"num_served\": 1,\n            \"id\": 13,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"ear of fresh corn\", [\"1 \", \"ear small ( 5-1/2\"], 1.0, \"ear small ( 5-1/2\"],\n            [\"butter (with salt)\", [\"1 1/3\", \"teaspoons\"], 0.0265, \"cup\"],\n            [\"black pepper\", [\"1/5\", \"teaspoons\"], 0.0666, \"tbsp\"],\n            [\"salt\", [\"1/5\", \"teaspoons\"], 0.004, \"cup\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Corn on the cob with butter, salt, and pepper\",\n            \"directions\": \"Boil or steam corn - cook to desired tenderness.\\u003Cbr/\\u003E\\u003Cbr/\\u003ESeason with butter, salt, and pepper.\",\n            \"num_served\": 1,\n            \"id\": 16,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"Corn\", [\"1 \", \"ear small ( 5-1/2\"], 1.0, \"ear small ( 5-1/2\"],\n            [\"black pepper\", [\"1/3\", \"teaspoons\"], 0.1, \"tbsp\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Corn on the cob with black pepper\",\n            \"directions\": \"Boil or steam corn - cook to desired tenderness.\\u003Cbr/\\u003E\\u003Cbr/\\u003ETo reduce sodium, do not use butter (usually contains salt) or add extra salt.\\u003Cbr/\\u003E\\u003Cbr/\\u003EAdd pepper to taste.\",\n            \"num_served\": 1,\n            \"id\": 17,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"Camembert cheese\", [\"3 \", \"tablespoons\"], 0.187, \"cup\"],\n            [\"Nabisco Ritz Crackers\", [\"1 \", \"serving\"], 1.0, \"serving\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Camembert cheese and crackers\",\n            \"directions\": \"Spread softened cheese (let ripen at room temperature for at least 30 minutes before eating) on crackers.\",\n            \"num_served\": 1,\n            \"id\": 18,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"Cheddar Cheese\", [\"1 \", \"cup diced\"], 1.0, \"cup diced\"],\n            [\"tortilla chips\", [\"16 \", \"oz\"], 16.0, \"oz\"],\n            [\"ear of fresh corn\", [\"1 \", \"cup\"], 1.0, \"cup\"],\n            [\"Tabasco Sauce\", [\"1 \", \"tsp\"], 1.0, \"tsp\"],\n            [\"brown onions\", [\"1/4\", \"cup chopped\"], 0.25, \"cup chopped\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Cheddar Cheese Nachos\",\n            \"directions\": \"Cover tortilla chips with cheese and onions (optional) and diced fresh tomatoes.\\u003Cbr/\\u003E\\u003Cbr/\\u003EAdd Tabasco Sauce or red taco sauce, too taste.\\u003Cbr/\\u003E\\u003Cbr/\\u003ECook in microwave oven until the cheese has melted. \",\n            \"num_served\": 4,\n            \"id\": 23,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"lamb shank\", [\"4 \", \"piece cooked excluding refuse ( yield from 1 lb raw meat with refuse)\"], 4.0, \"piece cooked excluding refuse ( yield from 1 lb raw meat with refuse)\"],\n            [\"dried thyme (fresh thyme if you have it)\", [\"1/3\", \"tsp\"], 0.3333, \"tsp\"],\n            [\"black pepper\", [\"1 \", \"teaspoons\"], 0.3333, \"tbsp\"],\n            [\"salt\", [\"1 \", \"teaspoons\"], 0.02083, \"cup\"],\n            [\"brown onions\", [\"3/4\", \"cup chopped\"], 0.75, \"cup chopped\"],\n            [\"olive oil\", [\"2 \", \"tablespoons\"], 2.0, \"tbsp\"],\n            [\"fresh garlic\", [\"2 \", \"tablespoons\"], 0.125, \"cup\"],\n            [\"dry rosemary (fresh rosemary if you have it)\", [\"1 1/2\", \"tablespoons\"], 1.5, \"tbsp\"],\n            [\"white wine\", [\"8 \", \"fl oz\"], 8.0, \"fl oz\"],\n            [\"saffron\", [\"1/2\", \"teaspoons\"], 0.152, \"tbsp\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Mark's Baked Lamb Shanks\",\n            \"directions\": \"Start by cutting off excess fat from the lamb shanks. You will notice clear membrane covering most of the meat; try to remove most of this membrane with a sharp knife - however, do not remove any of the meat, just the membrane.\\u003Cbr/\\u003E\\u003Cbr/\\u003ECoarsely chop the onion and garlic. Then add all ingredients to a covered oven baking dish. Depending on the size of the dish, the lamb shanks will probably be about 1/3 covered by liquid. Add enough water to cover about 1/2 to 2/3 of the lamb shanks.\\u003Cbr/\\u003E\\u003Cbr/\\u003EPlace the covered baking dish in the refrigerator for a time interval of 1 hour to overnight. If possible during this period, occasionally turn the lamb shanks in the liquid.\\u003Cbr/\\u003E\\u003Cbr/\\u003ETurn the oven on and set it to 325 degrees F. It is not really necessary to preheat the oven. Cook the covered lamb shanks for 1 1/2 to 2 hours. Then uncover the lamb shanks and let them cook for an additional 20 to 30 minutes to allow some of the liquid to dry up. You will want some liquid for serving the lamb shanks. You will want to serve the lamb shanks with:\\u003Cbr/\\u003E\\u003Cbr/\\u003E    * A large helping of steamed vegetables     * A side dish of a small serving of either noodles,       baked potato, or rice\\u003Cbr/\\u003E\\u003Cbr/\\u003EPlace one lamb shank on each plate with the vegetables and other side dish. Then spoon some of the liquid from the baking dish over the lamb shanks. This liquid will contain a fair amount of fat so while you should generously spoon it over the lamb shanks please avoid pouring it over the vegetables and side dish. Some of the liquid will flow on the plate into the vegetables and side dish which is OK and adds flavor.\",\n            \"num_served\": 4,\n            \"id\": 25,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"brown rice (cooked)\", [\"2 \", \"cup\"], 2.0, \"cup\"],\n            [\"soy sauce\", [\"2 \", \"tablespoons\"], 2.0, \"tbsp\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Brown rice\",\n            \"directions\": \"Cook rice as per directions. If you buy your brown rice in bulk at a health food store (a good idea!) add 1 part rice to 2 parts water and cook in either a rice cooker or a pan with a tightly fitting top - do not lift the lid while rice is cooking.\\u003Cbr/\\u003E\\u003Cbr/\\u003EIf you are not on a low sodium diet, add soy sauce to taste.\",\n            \"num_served\": 4,\n            \"id\": 26,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"white rice (cooked)\", [\"2 \", \"cup\"], 2.0, \"cup\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"White rice\",\n            \"directions\": \"Cook rice as per package directions.\\u003Cbr/\\u003E\\u003Cbr/\\u003EIf you buy your rice in bulk, cook with 1 part dry rice to 2 parts water. Cook in a tightly covered pan for 20 minutes or use a rice cooker.\",\n            \"num_served\": 4,\n            \"id\": 27,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"potatoes\", [\"1 \", \"potato large\"], 1.0, \"potato large\"],\n            [\"butter\", [\"1 \", \"tablespoons\"], 0.0625, \"cup\"],\n            [\"sour cream\", [\"1 \", \"tablespoons\"], 0.0625, \"cup\"],\n            [\"salt\", [\"1/6\", \"teaspoons\"], 0.003, \"cup\"],\n            [\"black pepper\", [\"1/10\", \"teaspoons\"], 0.02, \"tbsp\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Baked potato\",\n            \"directions\": \"Wash potatoes and bake in 325 oven for about 90 minutes.\\u003Cbr/\\u003E\\u003Cbr/\\u003EServe with salt, pepper, butter, and sour cream.\",\n            \"num_served\": 1,\n            \"id\": 30,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"redleaf lettuce\", [\"1/2\", \"cup shredded\"], 0.5, \"cup shredded\"],\n            [\"raw carrot\", [\"1/4\", \"cup chopped\"], 0.25, \"cup chopped\"],\n            [\"sweet (purple) raw onion\", [\"1/8\", \"onion\"], 0.125, \"onion\"],\n            [\"pealed raw cucumber\", [\"1/8\", \"cup pared chopped\"], 0.125, \"cup pared chopped\"],\n            [\"olive oil\", [\"1 \", \"tablespoons\"], 1.0, \"tbsp\"],\n            [\"red wine or balsamic vinegar\", [\"1 1/10\", \"tablespoons\"], 0.065, \"cup\"],\n            [\"black pepper\", [\"1/4\", \"teaspoons\"], 0.07, \"tbsp\"],\n            [\"salt\", [\"1/10\", \"teaspoons\"], 0.002, \"cup\"]\n        ],\n        \"recipe\": {\n            \"rating\": 3.0,\n            \"name\": \"Tossed summer salad\",\n            \"directions\": \"Wash lettuce, carrot, onion, and cucumber; cut into bite size pieces.\\u003Cbr/\\u003E\\u003Cbr/\\u003EAdd remaining ingredients and mix well.\",\n            \"num_served\": 1,\n            \"id\": 33,\n            \"num_ratings\": 2\n        }\n    },\n    {\n        \"items\": [\n            [\"egg\", [\"2 \", \"large\"], 2.0, \"large\"],\n            [\"bacon\", [\"2 \", \"slice cooked\"], 2.0, \"slice cooked\"],\n            [\"bread (1 slice)\", [\"1 \", \"oz\"], 1.0, \"oz\"],\n            [\"butter\", [\"1 \", \"teaspoons\"], 0.0208, \"cup\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.5,\n            \"name\": \"Eggs, bacon, and toast with butter\",\n            \"directions\": \"Fry eggs in half the butter. Cook bacon and drain.\\u003Cbr/\\u003E\\u003Cbr/\\u003EUse remaining butter on toast.\",\n            \"num_served\": 1,\n            \"id\": 34,\n            \"num_ratings\": 2\n        }\n    },\n    {\n        \"items\": [\n            [\"bread\", [\"4 \", \"slice small\"], 4.0, \"slice small\"],\n            [\"ground cinnamon\", [\"1/2\", \"teaspoons\"], 0.152, \"tbsp\"],\n            [\"ground nutmeg\", [\"1/2\", \"teaspoons\"], 0.152, \"tbsp\"],\n            [\"orange (just the peel)\", [\"1/2\", \"teaspoons\"], 0.152, \"tbsp\"],\n            [\"lemon (just the peel)\", [\"1/2\", \"teaspoons\"], 0.152, \"tbsp\"],\n            [\"egg yolks\", [\"1/4\", \"cup\"], 0.25, \"cup\"],\n            [\"maple syrup\", [\"1/2\", \"cup\"], 0.5, \"cup\"],\n            [\"baking soda\", [\"1/8\", \"tsp\"], 0.125, \"tsp\"],\n            [\"raisins\", [\"1/4\", \"cup packed\"], 0.25, \"cup packed\"],\n            [\"low fat milk\", [\"2 \", \"cup\"], 2.0, \"cup\"],\n            [\"vanilla extract\", [\"1 1/2\", \"teaspoons\"], 0.5, \"tbsp\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Bread Pudding\",\n            \"directions\": \"Cube bread and place in 13x9 inch pan. Combine cinnamon, nutmeg, orange and lemon peels, walnuts and raisins and sprinkle evenly over bread cubes. Beat egg yolks, maple syrup and baking soda. Scald the milk. Stir egg mixture and vanilla into milk and pur over bread mixture. Bake at 350 one hour or until golden.\",\n            \"num_served\": 4,\n            \"id\": 35,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"flour tortilla\", [\"2 \", \"serving\"], 2.0, \"serving\"],\n            [\"Cheddar cheese\", [\"1/8\", \"cup diced\"], 0.125, \"cup diced\"],\n            [\"black olives\", [\"4 \", \"jumbo\"], 4.0, \"jumbo\"],\n            [\"tomato (diced)\", [\"1/4\", \"cup\"], 0.25, \"cup\"],\n            [\"Olive oil\", [\"1 \", \"tablespoons\"], 1.0, \"tbsp\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Cheese quesadilla\",\n            \"directions\": \"If you are making two small quesadillas, you can cook them together in a large frying pan, or separately in a small pan. One large tortilla can be substituted for 2 8-inch tortillas.\\u003Cbr/\\u003E\\u003Cbr/\\u003EAdd oil to a pan and set heat for medium. Cook one side of the tortillas until the cooked side starts to slightly blacken.\\u003Cbr/\\u003E\\u003Cbr/\\u003EThe tomato and large olives should be coarsely chopped.\\u003Cbr/\\u003E\\u003Cbr/\\u003ETurn tortillas and add all ingredients, mixed, and being careful to keep ingredients away from the edges of the tortillas. Lower heat slightly, and cook until the cheese melts and the outer side of the tortillas slightly blacken.\",\n            \"num_served\": 1,\n            \"id\": 36,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"Salmon\", [\"1 \", \"fillet\"], 1.0, \"fillet\"],\n            [\"green onions (scallions)\", [\"1 \", \"cup chopped\"], 1.0, \"cup chopped\"],\n            [\"low fat milk\", [\"2 \", \"cup\"], 2.0, \"cup\"],\n            [\"olive oil\", [\"2 \", \"tablespoons\"], 2.0, \"tbsp\"],\n            [\"salt\", [\"1/3\", \"teaspoons\"], 0.0069, \"cup\"],\n            [\"black pepper\", [\"1/3\", \"teaspoons\"], 0.1, \"tbsp\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Salmon and Scallions Soup\",\n            \"directions\": \"Wash and chop scallions. Mix scallions with olive oil, salt, and pepper and start to cook over low heat for 1 minute, then add milk and cook under medim heat until the milk has boiled for one minute.\\u003Cbr/\\u003E\\u003Cbr/\\u003EWhile the milk is heating, remove the skin from the salmon and cut into small pieces.\\u003Cbr/\\u003E\\u003Cbr/\\u003EAdd the salmon to the lightly boiling milk, reduce heat to very low, cover, and cook for 10 minutes.\",\n            \"num_served\": 4,\n            \"id\": 37,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"canned tuna (in water, not oil), drained\", [\"1/2\", \"cup solid or chunks\"], 0.5, \"cup solid or chunks\"],\n            [\"frozen corn\", [\"1/4\", \"(0.5) cup\"], 0.25, \"(0.5) cup\"],\n            [\"frozen peas\", [\"1/4\", \"(0.5) cup\"], 0.25, \"(0.5) cup\"],\n            [\"dry pasta (any type)\", [\"1 \", \"cup\"], 1.0, \"cup\"],\n            [\"brown onions\", [\"1/3\", \"cup chopped\"], 0.3333, \"cup chopped\"],\n            [\"salt\", [\"1/4\", \"teaspoons\"], 0.005, \"cup\"],\n            [\"mayonnaise\", [\"2 \", \"tablespoons\"], 0.125, \"cup\"],\n            [\"black pepper\", [\"1/5\", \"teaspoons\"], 0.06667, \"tbsp\"],\n            [\"olive oil\", [\"1 \", \"tablespoons\"], 1.0, \"tbsp\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Pasta Salad with Tuna, Corn, and Peas\",\n            \"directions\": \"This can be a complete \\\"one dish meal\\\":\\u003Cbr/\\u003E\\u003Cbr/\\u003ECook pasta according to directions, drain, and cool.\\u003Cbr/\\u003E\\u003Cbr/\\u003ECook corn and peas according to package directions, rinse, and cool.\\u003Cbr/\\u003E\\u003Cbr/\\u003EFinely chop white onion.\\u003Cbr/\\u003E\\u003Cbr/\\u003EMix all ingredients and place in refrigerator for at least 15 minutes to cool.\",\n            \"num_served\": 2,\n            \"id\": 38,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"cheddar cheese\", [\"1/4\", \"cup sliced\"], 0.25, \"cup sliced\"],\n            [\"bread (preferably French bread)\", [\"2 \", \"slice small\"], 2.0, \"slice small\"],\n            [\"butter\", [\"1 \", \"tablespoons\"], 0.0625, \"cup\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Grilled Cheddar Cheese Sandwich\",\n            \"directions\": \"Melt butter in a skillet and brown one side of both pieces of bread.\\u003Cbr/\\u003E\\u003Cbr/\\u003ETurn over one slice, add cheese and cover with other slice of bread.\\u003Cbr/\\u003E\\u003Cbr/\\u003ETurn after the bottom is browned and serve when the other side is done. \",\n            \"num_served\": 1,\n            \"id\": 39,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"elbow macaroni\", [\"2 \", \"cup elbow shaped\"], 2.0, \"cup elbow shaped\"],\n            [\"olive oil\", [\"2 \", \"tablespoons\"], 2.0, \"tbsp\"],\n            [\"butter\", [\"1 1/3\", \"tablespoons\"], 0.08, \"cup\"],\n            [\"carrots\", [\"1/4\", \"cup chopped\"], 0.25, \"cup chopped\"],\n            [\"brown onions\", [\"1/4\", \"cup chopped\"], 0.25, \"cup chopped\"],\n            [\"mushrooms\", [\"1/2\", \"cup pieces or slices\"], 0.5, \"cup pieces or slices\"],\n            [\"broccoli\", [\"1/2\", \"cup flowerets\"], 0.5, \"cup flowerets\"],\n            [\"half-and-half\", [\"1 \", \"cup\"], 1.0, \"cup\"],\n            [\"Swiss cheese\", [\"3/4\", \"cup diced\"], 0.75, \"cup diced\"],\n            [\"egg\", [\"2 \", \"large\"], 2.0, \"large\"],\n            [\"salt\", [\"1 \", \"teaspoons\"], 0.02, \"cup\"],\n            [\"black pepper\", [\"1 \", \"teaspoons\"], 0.333, \"tbsp\"],\n            [\"raw shrimp\", [\"12 \", \"medium\"], 12.0, \"medium\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Shrimp Macaroni and Cheese\",\n            \"directions\": \"\\u003Cp\\u003ECook the elbow macaroni according to directions on package. Do not overcook. Heat oven to 375 degrees. While the elbow macaroni is cooking finely chop: carrots, onions, and broccoli. Thinly slice the mushrooms. Chop the cheese into small cubes. Cut the shrimp in half, lengthwise. Use half the butter to lightly cook the shrimp in a non-stick pan for two minutes and set aside. Use half the butter to grease a 9 by 13 inch baking pan. In a large bowl, beat the eggs and then add all remaining ingredients, including the cooked elbow macaroni. Mix everything together and pour into the baking dish. Bake covered for 30 minutes. Uncover and cook for another 10 minutes. Remove from oven and cool for 10 minutes before serving.\\u003Cp\\u003E\",\n            \"num_served\": 4,\n            \"id\": 50,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"canned soup (any of your favorites)\", [\"1 \", \"serving 1/3 cup\"], 1.0, \"serving 1/3 cup\"],\n            [\"Optional: broccoli (raw and chopped)\", [\"1/4\", \"cup chopped\"], 0.25, \"cup chopped\"],\n            [\"Optional: carrots (raw and chopped)\", [\"1/4\", \"cup chopped\"], 0.25, \"cup chopped\"],\n            [\"Optional: brussels sprouts (raw and chopped)\", [\"1/4\", \"cup\"], 0.25, \"cup\"],\n            [\"Optional: celery (raw and chopped)\", [\"1/4\", \"cup chopped\"], 0.25, \"cup chopped\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Canned Soup with Fresh Vegetables\",\n            \"directions\": \"\\u003Cp\\u003EThis is one of my favorite lunches that is easy to prepare, tastes great, and is healthy because of the fresh vegetables. Start by filling a covered sauce pan about 1/2 inch with water and place over medium to high heat. Starting with the 'tougher' vegetables first finely chop the vegetables and add to the water. Cover the sauce pan and cook for about 5 minutes, then add the canned soup and heat just until everything reaches a boil.\\u003C/p\\u003E\",\n            \"num_served\": 2,\n            \"id\": 44,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"sweet red onions (brown onions are fine to use also)\", [\"1/4\", \"cup chopped\"], 0.25, \"cup chopped\"],\n            [\"Olive Oil\", [\"5 \", \"tablespoons\"], 5.0, \"tbsp\"],\n            [\"Sriracha Hot Chili Sauce\", [\"1 1/2\", \"teaspoons\"], 0.5, \"tbsp\"],\n            [\"Optional: white wine\", [\"4 \", \"fl oz\"], 4.0, \"fl oz\"],\n            [\"Optional: zucchini squash (raw and chopped)\", [\"1/2\", \"cup chopped\"], 0.5, \"cup chopped\"],\n            [\"Optional: spinach (raw and chopped)\", [\"1/2\", \"cup\"], 0.5, \"cup\"],\n            [\"Optional: broccoli (raw and chopped)\", [\"1/2\", \"cup chopped\"], 0.5, \"cup chopped\"],\n            [\"fresh garlic (finely chopped)\", [\"1 1/3\", \"tablespoons\"], 0.08, \"cup\"],\n            [\"raw shrimp (peeled. defrost if frozen)\", [\"1/2\", \"cup\"], 0.5, \"cup\"],\n            [\"brown rice (cooked as per directions; white rice is also OK)\", [\"1 1/2\", \"cup\"], 1.5, \"cup\"],\n            [\"salt (to taste)\", [\"3/4\", \"teaspoons\"], 0.015, \"cup\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Spicy Shrimp with Vegetables and Rice\",\n            \"directions\": \"You can experiment with this recipe by adjusting the relative amounts of shrimp and vegetables. For a healthy meal I prefer using relatively few shrimp (slicing them in half lengthwise) and a lot of vegetables. Sometimes I use fewer fresh vegetables and more shrimp (and then leave the shrimp whole).\\n\\nThe white wine is optional. You can substitute some canned vegetable broth or water.\\n\\nPut half the olive oil in a heavy frying pan that has a lid (which we will use later). Heat the oil and cook the onion for about a minute before adding the chopped vegetables and then the hot chili sauce.\\n\\nAfter a few minutes add the finely chopped fresh garlic, give everything a good stir, and cook for another minute before adding the wine (or broth, or water). Stir again, cover the frying pan, reduce heat and slowly cook for another 5 minutes, stirring occasionally. Add the raw shrimp, stir, cover, and cook for another 5 minutes (stirring occasionally to make sure the shrimp get cooked).\\n\\nIf you use pre-cooked shrimp (not really recommended) then delay adding the defrosted cooked shrimp for a few minutes. You don't want to overcook already cooked shrimp.\\n\\nUncover the pan and add the cooked rice.  Keeping the heat low, stir the ingredients together and cook for another few minutes. Finally, add the remaining olive oil, stir well, turn off the heat, cover the pan and let everything sit for a few minutes before serving.\\n\\nYou can also serve the shrimp and vegetables on top of the cooked rice instead of mixing in the rice at the end of cooking the shrimp and vegetables.\\n\\nI like to keep frozen raw shrimp in the freezer. This recipe works well when I have vegetables need to use up. This recipe also works well when I have left over cooked rice to use up.\\n\\nIf I have leftovers for this recipe I like to make another meal by heating lightly in the microwave and adding some sour cream before eating. The sour cream adds a great flavor and makes leftovers taste different.\",\n            \"num_served\": 2,\n            \"id\": 42,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"brown onions\", [\"1 \", \"onion\"], 1.0, \"onion\"],\n            [\"sweet red pepper (bell pepper)\", [\"1 \", \"cup strips\"], 1.0, \"cup strips\"],\n            [\"olive oil\", [\"1/3\", \"cup\"], 20.0, \"tbsp\"],\n            [\"fresh garlic (peeled and chopped)\", [\"5\", \"clove\"], 0.2, \"cup\"],\n            [\"fresh basil (chopped)\", [\"5 \", \"(2) tbsp chopped\"], 5.0, \"(2) tbsp chopped\"],\n            [\"salt\", [\"1\", \"teaspoons\"], 0.01, \"cup\"],\n            [\"black pepper\", [\"1/2\", \"teaspoons\"], 0.1, \"tbsp\"],\n            [\"red pepper flakes\", [\"1\", \"teaspoons\"], 0.2, \"tbsp\"],\n            [\"canned chopped tomatoes\", [\"4\", \"cup\"], 1.8, \"cup\"],\n            [\"water\", [\"12 \", \"fl oz\"], 12.0, \"fl oz\"],\n            [\"dry pasta (uncooked spaghetti)\", [\"2 1/5\", \"cup\"], 2.2, \"cup\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Marinara Sauce\",\n            \"directions\": \"<p>Note that you can substitute fresh chopped tomatoes for all or some of the canned tomatoes.</p><p>Put 1/3 of the olive oil in a large heavy pan and start to heat to low temperature while chopping the onion and red bell peppers in a food processor. Chop either coarsely of finely depending on how smooth you want your Marinara Sauce.</p><p>Fry the onion and red bell peppers over medium-high heat for about 10 minutes, stirring often. While you are doing this peel the garlic and chop it finely. Lower the heat and add the garlic and continue stirring frequently for about 5 minutes.  While you are stirring open the canned tomatos (or sauce with diced tomatoes - your choice). With medium heat, add the tomato sauce and another 1/3 of the olive oil  and stir often to mix all the ingredients. After about 10 minutes, add half the water, stir well, reduce the heat and cover. I cook the sauce under very low heat for about 3 hours but you can reduce the cooking time if you need to. I set an alarm to go off every 20 minutes to remind me to stir the sauce and add a little bit of water if the sauce is very thick. About 5 minutes before turning off the heat I add the final 1/3 of the olive oil to the sauce. I usually let the sauce sit for 15 or 20 minutes after it is done before serving over pasta.</p><p>Cook your favorite pasta as per the directions on the package and serve with sauce.</p><p>Pro tip: this recipe is a lot of work so I usually make a very large quantity of Marinara Sauce and freeze some of it for future use.</p><p>This Marinara sauce is useful as a base for other recipes and is delicious to eat as-is.</p>\",\n            \"num_served\": 6,\n            \"id\": 41,\n            \"num_ratings\": 1\n        },\n        \"pictures\":\n            [\n                [\"marinara1\", \"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Peppers and onion\"],\n                [\"marinara2\", \"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Chop before<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;food processing\"],\n                [\"marinara3\", \"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Peppers and onion<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ready for cooking\"],\n                [\"marinara4\", \"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Peppers and onion<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fried in oil\"],\n                [\"marinara5\", \"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thinly slice/chop garlic\"],\n                [\"marinara6\", \"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Ready to eat with pasta\"]\n            ]\n\n    },\n    {\n        \"items\": [\n            [\"sweet potatoes\", [\"4 \", \"medium ( 2\"], 4.0, \"medium ( 2\"],\n            [\"Olive oil\", [\"6 \", \"tablespoons\"], 6.0, \"tbsp\"],\n            [\"salt (to taste)\", [\"1/3\", \"teaspoons\"], 0.007, \"cup\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Sweet Potato French Fries\",\n            \"directions\": \"\\u003Cp\\u003EThese fries are tasty and also very healthy since they are baked and not fried. Turn oven on and set to 400 degrees. Peal the sweet potatoes and cut them into the shape of french fries. Poor most of the olive oil onto a cookie sheet and roll the raw sweet potato french fries in the oil to cover them. Then spread out the french fries and dribble the remaining olive oil over the fries. Don't worry if the oven is fully pre-heated. The oven can continue to heat up while the fries are cooking. It will take about 30 minutes to cook the fries if you cut them sweet potato into thin pieces, and a little longer if the fries are thicker.\\u003C/p\\u003E \",\n            \"num_served\": 4,\n            \"id\": 43,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"chicken breast (raw, full, from a frying chicken)\", [\"1 \", \"unit ( yield from 1 lb ready-to-cook chicken)\"], 1.0, \"unit ( yield from 1 lb ready-to-cook chicken)\"],\n            [\"Sriracha Hot Chili Sauce\", [\"4 \", \"tsp or 1 packet\"], 4.0, \"tsp or 1 packet\"],\n            [\"honey\", [\"1/3\", \"cup\"], 0.3, \"cup\"],\n            [\"soy sauce\", [\"1 \", \"tablespoons\"], 1.0, \"tbsp\"],\n            [\"Coconut oil\", [\"2 \", \"tablespoons\"], 2.0, \"tbsp\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Sweet and Spicy Chicken Breasts\",\n            \"directions\": \"\\u003Cp\\u003EDepending on your tastes and what you already have in your kitchen, you can vary the flavor of this recipe a lot by using either Sriracha Hot Chili Sauce (substitute a smaller amount of Tabasco sauce if you are out of Sriracha) or prepared mustard. (Prepared mustard can be Dijon or plain yellow mustard.) My Favorite is mixing in a little of both Sriracha and Dijon mustard. Mix the Sriracha, mustard, honey, and soy sauce on a paper plate and coat the boanless and skinless chicken with this mixture. Heat the Coconut oil to medium high heat in a no-stick pan and add the chicken. Cook the chicken for about 7 or 8 minutes per side. Cut into the fattest piece of chicken to make sure it is done. Cook longer if necessary. Since this dish is flavorful and spicy, but also fairly high in fat and sugar, I like to pair this recipe with simple steamed vegetables. \\u003Cb\\u003EAs an appetizer:\\u003C/b\\u003E you can use chicken wings and make an appetizer. You will need to increase the amount of coconut oil so they are crisp.\\u003C/p\\u003E\",\n            \"num_served\": 2,\n            \"id\": 46,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"Small fillet of Salmon, thawed and skin removed\", [\"2 \", \"(0.5) fillet\"], 2.0, \"(0.5) fillet\"],\n            [\"Bread crumbs (Italian, plain, or seasoned)\", [\"1/4\", \"cup\"], 0.25, \"cup\"],\n            [\"Dijon Mustard (Yellow mustard is OK also)\", [\"3 \", \"tsp or 1 packet\"], 3.0, \"tsp or 1 packet\"],\n            [\"Coconut Oil\", [\"3 \", \"tablespoons\"], 3.0, \"tbsp\"],\n            [\"Salt and Pepper to taste\", [\"1/2\", \"teaspoons\"], 0.01, \"cup\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Fried Salmon with Dijon Mustard Breading\",\n            \"directions\": \"\\u003Cp\\u003EMix bread crumbs and mustard together on a small paper plate and coat the salmon fillets with this mixture. Heat coconut oil to medium high heat (preferably in a non-stick pan). Coconut oil is excellant to fry with as its nutritional value is not much affected by heat. place salmon fillets in the hot oil and cook for about 4 minutes. Then turn heat down to medium and turn over the fish. The top side of the fish will appear more crispy because that side of the fish will have absorbed most of the coconut oil and was cooked under higher heat. Serve with the crunchy side of the fish up. \\u003Ci\\u003ENote: you can substitute salmon steaks for fillets.\\u003C/i\\u003E\\u003C/p\\u003E\",\n            \"num_served\": 2,\n            \"id\": 45,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"Shiitake mushrooms\", [\"1/2\", \"cup pieces\"], 0.5, \"cup pieces\"],\n            [\"Kale (or substitute green lettuce)\", [\"2 \", \"cup chopped\"], 2.0, \"cup chopped\"],\n            [\"tofu (soft)\", [\"2/3\", \"cup ( 1/2\"], 0.6, \"cup ( 1/2\"],\n            [\"cashew (nuts)\", [\"1/3\", \"cup halves and whole\"], 0.3, \"cup halves and whole\"],\n            [\"Sesame Oil\", [\"1 \", \"tablespoons\"], 1.0, \"tbsp\"],\n            [\"Soy sauce\", [\"1 \", \"tablespoons\"], 1.0, \"tbsp\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Oriental Kale and Mushroom Salad\",\n            \"directions\": \"\\u003Cp\\u003EYou can substitute chopped green lettuce for kale. If you are using fresh shiitake mushrooms then wash them and dry them. If you are using dried shiitake mushrooms then soak them in just enough water to cover them for about a half hour until they are soft. Mix all the ingredients and serve.\\u003C/p\\u003E\",\n            \"num_served\": 4,\n            \"id\": 47,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"brown rice\", [\"3/4\", \"cup\"], 0.75, \"cup\"],\n            [\"hoison sauce\", [\"1 \", \"tablespoons\"], 1.0, \"tbsp\"],\n            [\"soy sauce\", [\"1 1/2\", \"teaspoons\"], 0.5, \"tbsp\"],\n            [\"canola oil\", [\"1 1/2\", \"teaspoons\"], 0.5, \"tbsp\"],\n            [\"sesame oil\", [\"1 1/2\", \"teaspoons\"], 0.5, \"tbsp\"],\n            [\"raw shrimp\", [\"8 \", \"medium\"], 8.0, \"medium\"],\n            [\"egg\", [\"1 \", \"large\"], 1.0, \"large\"],\n            [\"fresh ginger\", [\"4 \", \"tsp\"], 4.0, \"tsp\"],\n            [\"frozen peas\", [\"1 \", \"cup\"], 1.0, \"cup\"],\n            [\"carrots (raw and finely chopped)\", [\"3/4\", \"cup chopped\"], 0.75, \"cup chopped\"],\n            [\"brown onions (finely chopped)\", [\"1/4\", \"cup chopped\"], 0.25, \"cup chopped\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Simple Shrimp Fried Rice\",\n            \"directions\": \"\\u003Cp\\u003EThis tasty and delicious recipe has two sources of protein: shrimp and egg. I vary the ratio of shrimp and egg, depending on what I feel like. You can leave out the shrimp and double the number of eggs also. The brown rice takes a while to cook so start that in your rice cooker (or in a tightly covered pan on the stove) cooking as per the package directions. Optional: when done put the rice in a large bowl to cool off. After the rice is cooked: Put about half the sesame oil in a non-stick pan, heat, beat the eggs and fry. Set aside on a plate for later. Use the rest of the sesame oil to fry the shrimp. When they are done, put them on the plate with the cooked egg. Hopefully while frying the eggs and shrimp you have already removed the skin and chopped the onion and ginger, and finely chopped the carrots - if not do that now. Add canola oil to the non-stick pan and cook the finely sliced or chopped up ginger and carrots for two minutes - then add the onions and frozen peas and continue frying for a two minutes. Add the rice, and fry everything in the pan for another minute then add the already cooked egg and shrimp and keep frying for another minute. Then turn off the heat. Mix in the hoison and soy sauces and stir well.\\u003Cp\\u003E\\u003Cp\\u003EServe immediately.\\u003C/p\\u003E\",\n            \"num_served\": 2,\n            \"id\": 49,\n            \"num_ratings\": 1\n        }\n    },\n    {\n        \"items\": [\n            [\"Olive oil\", [\"1 1/2\", \"tablespoons\"], 1.5, \"tbsp\"],\n            [\"Butter\", [\"1 \", \"teaspoons\"], 0.02, \"cup\"],\n            [\"Green onions (or yellow onions) chopped\", [\"1/10\", \"cup chopped\"], 0.1, \"cup chopped\"],\n            [\"fresh garlic (peeled and sliced)\", [\"3 \", \"teaspoons\"], 0.06, \"cup\"],\n            [\"white wine\", [\"3 \", \"fl oz\"], 3.0, \"fl oz\"],\n            [\"parsley (chopped)\", [\"1 2/3\", \"tablespoons\"], 0.1, \"cup\"],\n            [\"mushrooms (sliced)\", [\"1/5\", \"cup pieces or slices\"], 0.2, \"cup pieces or slices\"],\n            [\"dry pasta (Linguine, cooked. can substitute other types of pasta)\", [\"1 1/2\", \"cup\"], 1.5, \"cup\"],\n            [\"canned clams (with juice. clams can be whole or diced)\", [\"1 \", \"cup ( with liquid and clams)\"], 1.0, \"cup ( with liquid and clams)\"]\n        ],\n        \"recipe\": {\n            \"rating\": 2.0,\n            \"name\": \"Linguine with Clam Sauce\",\n            \"directions\": \"This is a fast recipe to make, but you will be cooking with two pans at once. Add more or less clams and juice to the recipe depending on your taste for clams. If you don't use many clams then you might have to add a little water to the frying pan while cooking. Start a large pan of salted water to boil. Peel and thinly slice the garlic and chop the onions. Heat olive oil and butter in a frying pan and add the onion and cook for a minute, stirring as needed. Then add the garlic. The water should be boiling so add the Linguine pasta (or, you can substitute other types of pasta). Note: you don't want to fully cook the pasta; when you taste it it should have some slight crunch since we will re-cook it with the sauce. If the recipe for cooking the pasta indicates a 10 minute cooking time, only boil the pasta for about 8 or 9 minutes. Add the white wine, sliced mushrooms, and the clam juice (not the clams, yet!) to the frying pan and simmer for at least 5 minutes. Add the drained pasta, parsley and simmer for a few more minutes. Finally, add the clams and simmer for another 3 or 4 minutes and then immediately serve. The dish is done when the pasta is fully cooked - taste it before serving to check for doneness.\\u003Cp\\u003EOptional: serve with parmesan cheese\\u003C/p\\u003E\",\n            \"num_served\": 2,\n            \"id\": 48,\n            \"num_ratings\": 1\n        }\n    }\n]\n"
  },
  {
    "path": "cooking_recipes/data/tiny.json",
    "content": "    {\"name\":\"Balsamic Mushrooms\",\n        \"num_served\":3,\n        \"ingredients\":\n            [{\"name\":\"mushrooms\",\n                \"amount\":\"12\",\n                \"units\":\"medium\",\n                \"description\":\"Mushrooms\"},\n                {\"name\":\"balsamic vinegar\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"cup\",\n                    \"description\":\"Balsamic vinegar\"},\n                {\"name\":\"red wine\",\n                    \"amount\":\"1/8\",\n                    \"units\":\"cup\",\n                    \"description\":\"Red wine\"}],\n        \"directions\":\n            [\"Place all ingredients in a (preferably nonstick) pan and let sit for a few minutes.\",\n                \"Then cook covered over medium heat for about three minutes until they are soft.\",\n                \"Remove the cover and cook until the liquid is almost gone, then serve.\"]}\n"
  },
  {
    "path": "cooking_recipes/data/vegetarian.json",
    "content": "[\n    {\"name\":\"Balsamic Mushrooms\",\n        \"num_served\":3,\n        \"ingredients\":\n            [{\"name\":\"mushrooms\",\n                \"amount\":\"12\",\n                \"units\":\"medium\",\n                \"description\":\"Mushrooms\"},\n                {\"name\":\"balsamic vinegar\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"cup\",\n                    \"description\":\"Balsamic vinegar\"},\n                {\"name\":\"red wine\",\n                    \"amount\":\"1/8\",\n                    \"units\":\"cup\",\n                    \"description\":\"Red wine\"}],\n        \"directions\":\n            [\"Place all ingredients in a (preferably nonstick) pan and let sit for a few minutes.\",\n                \"Then cook covered over medium heat for about three minutes until they are soft.\",\n                \"Remove the cover and cook until the liquid is almost gone, then serve.\"]},\n    {\"name\":\"Barbecued Eggplant\",\n        \"num_served\":2,\n        \"ingredients\":\n            [{\"name\":\"eggplant\",\n                \"amount\":\"1\",\n                \"units\":\"\",\n                \"description\":\"eggplant\"},\n                {\"name\":\"barbecue sauce\",\n                \"amount\":\"1/4\",\n                \"units\":\"cup\",\n                \"description\":\"Eggplant Barbecue Sauce salt\"}],\n        \"directions\":\n            [\"Preheat the oven to 350 degrees. Peel and cut the eggplant into 1/3-inch  thick slices. Salt the slices. Place on a rack or colander and let stand  for 30 minutes. Wipe off salt with a clean kitchen towel. Lay the slices  out on a lightly oiled baking sheet. Brush on barbecue sauce. Bake for 15  minutes. Turn over, brush with barbecue sauce, and bake for 15 minutes more  or until the eggplant is tender but not mushy.\"]},\n    {\"name\":\"Lemon Sesame Broccoli\",\n        \"num_served\":4,\n        \"ingredients\":\n            [{\"name\":\"broccoli\",\n                \"amount\":\"2\",\n                \"units\":\"cup\",\n                \"description\":\"Broccoli\"},\n                {\"name\":\"sesame oil\",\n                    \"amount\":\"2\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"Sesame oil\"},\n                {\"name\":\"lemon juice (fresh)\",\n                    \"amount\":\"1/2\",\n                    \"units\":\"tablespoon\",\n                    \"description\":\"fresh lemon juice\"},\n                {\"name\":\"black pepper\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"black pepper\"},\n                {\"name\":\"salt\", \"amount\":\"1\", \"units\":\"teaspoon\", \"description\":\"Salt\"}],\n        \"directions\":\n            [\"Cut flowerets off of the broccoli and cut into small pieces.\",\n                \"Cut the stems into very thin round pieces so they will cook quickly.\",\n                \"Put the broccoli stems into  boiling water in saucepan, cover and cook for 3 minutes.\",\n                \"Add flowerets, cover and cook 5 minutes, and then drain all broccoli and place in a serving bowl.\",\n                \"Mix broccoli well with the sesame oil, lemon juice, salt, and black pepper. Eat immediately\"]},\n    {\"name\":\"Cauliflower and Broccoli Pizza Crust\",\n        \"num_served\":4,\n        \"ingredients\":\n            [{\"name\":\"cauliflower\",\n                \"amount\":\"5\",\n                \"units\":\"cup\",\n                \"description\":\"Broccoli\"},\n        {\"name\":\"Optional: broccoli\",\n            \"amount\":\"1\",\n            \"units\":\"cup\",\n            \"description\":\"Broccoli\"},\n                {\"name\":\"fresh garlic (peeled and coarsely chopped)\",\n                    \"amount\":\"1\",\n                    \"units\":\"\",\n                    \"description\":\"Cloves garlic,crushed\"},\n                {\"name\":\"parmesan cheese\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"cup\",\n                    \"description\":\"parmesan cheese\"},\n                {\"name\":\"mozzarella cheese (finely chopped)\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"cup\",\n                    \"description\":\"mozzarella cheese\"},\n                {\"name\":\"dried basil\",\n                    \"amount\":\"1\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"basil\"},\n                {\"name\":\"black pepper\",\n                    \"amount\":\"1/4\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"black pepper\"},\n                {\"name\":\"salt\", \"amount\":\"1/4\", \"units\":\"teaspoon\", \"description\":\"Salt\"},\n                {\"name\":\"Optional: red pepper flakes\", \"amount\":\"1/2\", \"units\":\"teaspoon\", \"description\":\"red pepper flakes\"},\n                {\"name\":\"egg\", \"amount\":\"1\", \"units\":\"\", \"description\":\"egg\"},\n                {\"name\":\"olive oil\",\n                    \"amount\":\"1/2\",\n                    \"units\":\"teaspoon\",\n                    \"description\":\"olive oil\"}\n            ],\n        \"directions\":\n            [\"This crust contains no wheat or other grains: very healthy and very tasty!\",\n                \"You will need parchment (baking) paper to make this recipe. Preheat oven to 425F. Place either a pizza stone or a large baking pan in the oven. Note: 1 medium large cauliflower is about 5 cups, coarsely chopped.\",\n             \"This recipe is just for the pizza crust. It is delicious! The broccoli is optional; in its place you can use a little more cauliflower. However, this is a good way to get people (kids!) to eat vegetables, so sneak in a little broccoli.\",\n                \"Chop up the cauliflower, broccoli, and garlic. Put in a food processor and spend a few minutes getting it all chopped to a very fine consistency. Transfer to a bowl.\",\n                \"This finely chopped mixture now needs to be broken down by cooking in a microwave oven for about 3 minutes. Cover the bowl in the microwave oven.\",\n                \"You will need to get the water out of the mixture: get a clean dish towel and spread it over a large cutting board. Dump the hot mixture in the center of the towel and then let it sit for about 5 minutes to cool down.\",\n                \"Carefully fold up the dish towel to avoid spilling any of the mixture and over the sink \\\"wring out\\\" as much water as you can. I spend about two minutes doing this.\",\n                \"Return the (almost dry) mixture to the bowel (wipe it out first if there is much moisture in it) and add the mozzarella cheese, parmesan cheese, dried basil, peper, salt, and (optional) red pepper flakes.\",\n                \"Blend everything together, then add a raw egg and mix together.\",\n                \"Cut a piece of parchment paper, put it on a cutting board and poor the olive oil in the center. You should have the bowl of mixed ingredients right beside the parchment paper for easy access.\",\n                \"With your (washed) hands, spread out the oil on the parchment paper into abut a 10 inch circle, pick up the mixture in the bowl with your hands and place it on the paper. Push the mixture down to make a circle about 3/8 of an inch high. With your fingers, push the edges of the pizza dough back towards the center a bit if there are areas where the material is thin. Try to get the same height.\",\n                \"You will carefully slide the parchment paper onto the pizza stone (or baking sheet) in the oven. I don't worry too much about letting heat out of the oven so I do this process slowly and carefully: to avoid burning myself I like to pull the oven rack abut 1/3 of the way out of the oven to make it safer to slide the parchment paper onto the pizza stone.\",\n                \"Shut the oven and cook for about 10 minutes. It will be cooked more later after you put sauce on the crust.\",\n                \"Take the entire pizza stone (or baking sheet) out of the oven and place it on top of the stove. Add your favorite pizza sauce and toppings and then put it back in the oven and bake the pizza for another 8 minutes.\",\n                \"IDEAS FOR PIZZA TOPPING: I like to use a small amount of marinara sauce, chopped onion, and cheese. The pizza crust is very good on its own, so you can go light on the toppings.\"],\n        \"pictures\":\n            [\n                [\"cauliflower_pizza_crust_1\", \"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Cooling crust mixture\"],\n                [\"cauliflower_pizza_crust_2\", \"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Crust formed, in oven\"],\n                [\"cauliflower_pizza_crust_3\", \"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Pizza topping added.\"],\n                [\"cauliflower_pizza_crust_4\", \"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Cooked pizza\"]\n            ]\n    }\n\n\n\n]\n"
  },
  {
    "path": "cooking_recipes/data.py",
    "content": "import json\n\ndef process_json(fpath):\n    with open(fpath, 'r') as f:\n        data = json.load(f)\n\n    for d in data:\n        with open(f\"text_data/{d['name']}.txt\", 'w') as f:\n            f.write(\"Recipe name: \" + d['name'] + '\\n\\n')\n            f.write(\"Number of servings: \" + str(d['num_served']) + '\\n\\n')\n            ingrediants = [\"  \" + str(ii['amount']) + ' ' + ii['units'] + ' ' + ii['description']\n                           for ii in d['ingredients']]\n            f.write(\"Ingredients:\\n\" + \"\\n\".join(ingrediants) + '\\n\\n')\n            f.write(\"Directions: \" + ' '.join(d['directions']) + '\\n')\n\nif __name__ == \"__main__\":\n    process_json('data/vegetarian.json')\n    process_json('data/desert.json')\n    process_json('data/fish.json')\n    process_json('data/meat.json')\n    process_json('data/misc.json')\n"
  },
  {
    "path": "cooking_recipes/recipe_generator.py",
    "content": "# This example uses code from https://python.langchain.com/docs/get_started\n\n# on macOS: pip install faiss-cpu\n\nfrom langchain_community.vectorstores import FAISS\nfrom langchain.text_splitter import RecursiveCharacterTextSplitter\nfrom langchain_openai import OpenAIEmbeddings\nfrom langchain_core.prompts import ChatPromptTemplate\nfrom langchain_openai import ChatOpenAI\nfrom langchain_community.document_loaders import DirectoryLoader\n\nllm = ChatOpenAI()\n\nembeddings = OpenAIEmbeddings(model=\"text-embedding-3-large\")\n\ntext_splitter = RecursiveCharacterTextSplitter()\n#documents = text_splitter.split_documents(docs)\n\n\nembeddings = OpenAIEmbeddings()\n\nloader = DirectoryLoader('./text_data/', glob=\"**/*.txt\")\ndocuments = loader.load()\n\nvector = FAISS.from_documents(documents, embeddings)\n\nfrom langchain.chains import create_retrieval_chain\n\nfrom langchain.chains.combine_documents import create_stuff_documents_chain\n\nprompt = ChatPromptTemplate.from_template(\"\"\"Answer the following question based only on the provided context:\n\n<context>\n{context}\n</context>\n\nQuestion: {input}\"\"\")\n\ndocument_chain = create_stuff_documents_chain(llm, prompt)\n\n\nretriever = vector.as_retriever()\nretrieval_chain = create_retrieval_chain(retriever, document_chain)\n\nresponse = retrieval_chain.invoke({\"input\": \"Create a new recipe using both Broccoli\"})\nprint(response[\"answer\"])\n\nresponse = retrieval_chain.invoke({\"input\": \"Create a recipe using Beans, Rice, and Chicken\"})\nprint(response[\"answer\"])\n\n"
  },
  {
    "path": "cooking_recipes/text_data/Aioli Sauce.txt",
    "content": "Recipe name: Aioli Sauce\n\nNumber of servings: 8\n\nIngredients:\n  3 unit Egg yolks\n  4 clove Cloves garlic\n  1 tablespoon Lemon juice\n  1/3 teaspoon Salt\n  1/4 teaspoon Tabasco\n  1/4 teaspoon Worcestershire sauce\n  1/4 teaspoon Freshly ground black pepper\n  1/4 cup Mayonnaise\n  3/4 cup Extra virgin olive oil\n  3 tablespoon Boiling water\n\nDirections: Blend together the egg yolks, garlic, lemon, salt and pepper in a blender or food processor for about 2 minutes. Very slowly add the oil and mayonnaise while continuing to blend the mixture. Add the seasonings and mix together. Add the boiling water, stir well, and refrigerate for later use.\n"
  },
  {
    "path": "cooking_recipes/text_data/Apple Muffins.txt",
    "content": "Recipe name: Apple Muffins\n\nNumber of servings: 10\n\nIngredients:\n  1  Egg\n  1/2 cup Milk\n  1/4 cup Vegetable oil\n  1/2 cup Applesauce\n  1 1/2 cup Flour\n  3/4 cup Sugar\n  2 teaspoon Baking powder\n  1/2 teaspoon Cinnamon\n  1/2 teaspoon Salt\n\nDirections: preheat oven to 375F. Beat egg in a bowl and then stir in milk, oil, and the applesauce. Then mix in the flour, most of the brown sugar, baking powder, cinnamon, and salt until everything is moist. Place batter in paper baking cups - about half to two thirds full. Sprinkle remainder of brown sugar top of muffins. Bake 25 to 30 minutes.\n"
  },
  {
    "path": "cooking_recipes/text_data/Balsamic Mushrooms.txt",
    "content": "Recipe name: Balsamic Mushrooms\n\nNumber of servings: 3\n\nIngredients:\n  12 medium Mushrooms\n  1/4 cup Balsamic vinegar\n  1/8 cup Red wine\n\nDirections: Place all ingredients in a (preferably nonstick) pan and let sit for a few minutes. Then cook covered over medium heat for about three minutes until they are soft. Remove the cover and cook until the liquid is almost gone, then serve.\n"
  },
  {
    "path": "cooking_recipes/text_data/Barbecued Chicken.txt",
    "content": "Recipe name: Barbecued Chicken\n\nNumber of servings: 2\n\nIngredients:\n  2 unit Boneless chicken breasts\n  2 tablespoon Soy sauce\n  2 tablespoon White wine\n  1 tablespoon Vegetable oil\n  1 tablespoon Cornstarch\n\nDirections: Mix together all of the sauce ingredients and pour half the sauce over chicken, coating the chicken on all sides. Let this stand for a while and then put the chicken on your barbeque (charcoal or gas grill works fine). Brush the sauce over the top of the chicken and grill for about 10 minutes, then turn over and apply more barbeque sauce on the top Be careful to not burn the chicken. Before removing from the grill, cut into a thick piece and make sure it is done.\n"
  },
  {
    "path": "cooking_recipes/text_data/Barbecued Eggplant.txt",
    "content": "Recipe name: Barbecued Eggplant\n\nNumber of servings: 2\n\nIngredients:\n  1  eggplant\n  1/4 cup Eggplant Barbecue Sauce salt\n\nDirections: Preheat the oven to 350 degrees. Peel and cut the eggplant into 1/3-inch  thick slices. Salt the slices. Place on a rack or colander and let stand  for 30 minutes. Wipe off salt with a clean kitchen towel. Lay the slices  out on a lightly oiled baking sheet. Brush on barbecue sauce. Bake for 15  minutes. Turn over, brush with barbecue sauce, and bake for 15 minutes more  or until the eggplant is tender but not mushy.\n"
  },
  {
    "path": "cooking_recipes/text_data/Barbecued Salmon with Basil.txt",
    "content": "Recipe name: Barbecued Salmon with Basil\n\nNumber of servings: 4\n\nIngredients:\n  4 unit Salmon steaks\n  2 tablespoon Olive oil\n  1 teaspoon Dried and crushed basil\n  1  Lemon wedges\n\nDirections: Mix the juice from half the lemon, olive oil and basil and thoroughly coat the salmon with this mixture. Cook the salmon over hot coals (or a gas grill) for about 5 minutes per side. Remove fish from barbeque and test for doneness. Serve with the remaining half lemon cut into thin wedges.\n"
  },
  {
    "path": "cooking_recipes/text_data/Basil Vinaigrette Salad Dressing.txt",
    "content": "Recipe name: Basil Vinaigrette Salad Dressing\n\nNumber of servings: 12\n\nIngredients:\n  1/4 cup balsamic vinegar\n  1/4 teaspoon dried basil (or 1/2 tsp fresh)\n  1/4 teaspoon Dried oregano (or 1/2 tsp fresh)\n  1 tablespoon Olive Oil\n\nDirections: Mix all ingredients in a bowl. Let sit at least 2 or 3 minutes before using on a salad. Refrigerate extra dressing for future use.\n"
  },
  {
    "path": "cooking_recipes/text_data/Black Bean Dip.txt",
    "content": "Recipe name: Black Bean Dip\n\nNumber of servings: 6\n\nIngredients:\n  2 cup Refried Black Beans (see rec.)\n  1/4 cup Sour cream\n  1 teaspoon Ground cumin\n  1/2 cup Salsa\n\nDirections: Use either a food processor or a mixing bowl and hand mixer to make this appetizer. Blend the black beans and cumin for at least one minute until the mixture is fairly smooth. Stir in salsa and sour cream and lightly mix. Serve immediately or store in the refrigerator.\n"
  },
  {
    "path": "cooking_recipes/text_data/Borscht.txt",
    "content": "Recipe name: Borscht\n\nNumber of servings: 4\n\nIngredients:\n  2 cup Beets\n  1  Onion\n  2 clove Garlic clove\n  2 quart Water\n  2 teaspoon Salt\n  3 tablespoon Sugar\n  1/4 cup Lemon juice\n\nDirections: Wash and peel beets and thickly slice so they cook faster. Peel the onion and garlic cloves. Leave the onion and garlic cloves whole since we will be removing them later. Mix everything except the lemon juice and sugar in a large covered pot and cook over low heat for one hour. Add the sugar and lemon juice.  Cook an additional thirty minutes, remove from heat and let cool. Discard the onion and garlic and use a hand mixer to blend into a cold soup. Optional: place a small spoonful of sour cream on top of each serving.\n"
  },
  {
    "path": "cooking_recipes/text_data/Brined Barbecued Chicken.txt",
    "content": "Recipe name: Brined Barbecued Chicken\n\nNumber of servings: 2\n\nIngredients:\n  2 unit Boneless chicken breasts\n  4 tablespoon Soy sauce\n  4 tablespoon White wine\n  2 tablespoon Vegetable oil\n  2 tablespoon Cornstarch\n\nDirections: Mix together all of the sauce ingredients. Put half the sauce in a large pan of cold water, add two additional tablespoons of salt, and stir. Add the chicken and store in the refrigerator overnight. Before barbequeing, remove the chicken from the brine solution and pour half of the sauce (made the day before) over chicken, coating the chicken on all sides. Put the chicken on your barbeque (charcoal or gas grill works fine). Brush the sauce over the top of the chicken and grill for about 10 minutes, then turn over and apply more barbeque sauce on the top Be careful to not burn the chicken. Before removing from the grill, cut into a thick piece and make sure it is done. NOTE: it is important to use cold water for brining and immediately refrigerate the brine after adding the chicken.\n"
  },
  {
    "path": "cooking_recipes/text_data/Cauliflower and Broccoli Pizza Crust.txt",
    "content": "Recipe name: Cauliflower and Broccoli Pizza Crust\n\nNumber of servings: 4\n\nIngredients:\n  5 cup Broccoli\n  1 cup Broccoli\n  1  Cloves garlic,crushed\n  1/4 cup parmesan cheese\n  1/4 cup mozzarella cheese\n  1 teaspoon basil\n  1/4 teaspoon black pepper\n  1/4 teaspoon Salt\n  1/2 teaspoon red pepper flakes\n  1  egg\n  1/2 teaspoon olive oil\n\nDirections: This crust contains no wheat or other grains: very healthy and very tasty! You will need parchment (baking) paper to make this recipe. Preheat oven to 425F. Place either a pizza stone or a large baking pan in the oven. Note: 1 medium large cauliflower is about 5 cups, coarsely chopped. This recipe is just for the pizza crust. It is delicious! The broccoli is optional; in its place you can use a little more cauliflower. However, this is a good way to get people (kids!) to eat vegetables, so sneak in a little broccoli. Chop up the cauliflower, broccoli, and garlic. Put in a food processor and spend a few minutes getting it all chopped to a very fine consistency. Transfer to a bowl. This finely chopped mixture now needs to be broken down by cooking in a microwave oven for about 3 minutes. Cover the bowl in the microwave oven. You will need to get the water out of the mixture: get a clean dish towel and spread it over a large cutting board. Dump the hot mixture in the center of the towel and then let it sit for about 5 minutes to cool down. Carefully fold up the dish towel to avoid spilling any of the mixture and over the sink \"wring out\" as much water as you can. I spend about two minutes doing this. Return the (almost dry) mixture to the bowel (wipe it out first if there is much moisture in it) and add the mozzarella cheese, parmesan cheese, dried basil, peper, salt, and (optional) red pepper flakes. Blend everything together, then add a raw egg and mix together. Cut a piece of parchment paper, put it on a cutting board and poor the olive oil in the center. You should have the bowl of mixed ingredients right beside the parchment paper for easy access. With your (washed) hands, spread out the oil on the parchment paper into abut a 10 inch circle, pick up the mixture in the bowl with your hands and place it on the paper. Push the mixture down to make a circle about 3/8 of an inch high. With your fingers, push the edges of the pizza dough back towards the center a bit if there are areas where the material is thin. Try to get the same height. You will carefully slide the parchment paper onto the pizza stone (or baking sheet) in the oven. I don't worry too much about letting heat out of the oven so I do this process slowly and carefully: to avoid burning myself I like to pull the oven rack abut 1/3 of the way out of the oven to make it safer to slide the parchment paper onto the pizza stone. Shut the oven and cook for about 10 minutes. It will be cooked more later after you put sauce on the crust. Take the entire pizza stone (or baking sheet) out of the oven and place it on top of the stove. Add your favorite pizza sauce and toppings and then put it back in the oven and bake the pizza for another 8 minutes. IDEAS FOR PIZZA TOPPING: I like to use a small amount of marinara sauce, chopped onion, and cheese. The pizza crust is very good on its own, so you can go light on the toppings.\n"
  },
  {
    "path": "cooking_recipes/text_data/Chicken Cacciatore.txt",
    "content": "Recipe name: Chicken Cacciatore\n\nNumber of servings: 4\n\nIngredients:\n  2  Pieces chicken\n  2  Crushed tomatoes\n  2 teaspoon Sugar\n  2 teaspoon Salt\n  1 teaspoon Pepper\n  1 teaspoon Butter\n  2 tablespoon Oil\n  1 teaspoon Rosemary\n\nDirections: Wash the chicken and cook it for 5 minutes a side with butter, oil, and rosemary in a large skillet under medium heat. Then cut up the tomatoes and mix in with the cooking chicken chicken. Add the basil, sage, rosemary and parsley, sugar, salt, and pepper. Cover and cook on low to medium heat for about two hours. Check chicken for doneness before serving\n"
  },
  {
    "path": "cooking_recipes/text_data/Chicken Schnitzel.txt",
    "content": "Recipe name: Chicken Schnitzel\n\nNumber of servings: 6\n\nIngredients:\n  2 pounds Chicken Breasts\n  4 unit Eggs\n  1.5 cup Flour\n  1 cup Bread Crumbs\n  5 tablespoon Vegetable oil\n\nDirections: Slice Chicken Breasts 2 parts so each piece is as large an area and thin as possible. Remove skin and any excess fat. Optional: place on a cutting board and pound flatter. Dip chicken in beaten eggs, roll in flour, then in bread crumbs. Heat oil in a (preferably nonstick) frying pan. Fry the Schnitzel on each side until golden brown and the chicken is done (cut into chicken to make sure it is cooked).\n"
  },
  {
    "path": "cooking_recipes/text_data/Chicken with Cream of Mushroom Soup.txt",
    "content": "Recipe name: Chicken with Cream of Mushroom Soup\n\nNumber of servings: 2\n\nIngredients:\n  2  Pieces chicken\n  1 tablespoon Oil\n  1/2 cup Pint sour cream\n  1/2 teaspoon Salt\n  1/4 teaspoon Pepper\n  1 can Cream of mushroom soup (I use Campbell's)\n\nDirections: Heat oil in a non-stick frying pan with medium heat. Lightly brown the chicken, about  minutes per side. Mix in the salt, pepper, and soup, then cover, and cook under low heat for about 30 minutes, stirring occasionally. Mix in the sour cream, cook for another 5 minutes. Make sure the chicken is done and tender before serving.\n"
  },
  {
    "path": "cooking_recipes/text_data/Chinese Shrimp with Zucchini Squash.txt",
    "content": "Recipe name: Chinese Shrimp with Zucchini Squash\n\nNumber of servings: 2\n\nIngredients:\n  14 medium-large medium shrimp\n  1 tablespoon Cornstarch\n  1/2 teaspoon Salt\n  1/2 teaspoon Pepper\n  2 cup Zucchini\n  2 tablespoon Oil\n  1 tablespoon Sherry\n  1 tablespoon Soy sauce\n\nDirections: I vary the proportion of shrimp to Zucchini squash depending on both my tastes and how much shrimp and Zucchini I have on-hand that I want to use up. Shell and chop the shrimps into three or four pieces and roll in cornstarch, salt and  pepper - use a small bowl for this. Wash the zucchini and coarsely chop. Heat the oil in a non-stick pan and fry Zucchini until it is mostly done before adding the shrimp and the sherry. When the shrimp are done then mix in the soy sauce and serve immediately. Note: I like to eat this dish as-is but you might want to also try serving it with cooked Chinese style noodles or even a small amount of cooked Italian pasta.\n"
  },
  {
    "path": "cooking_recipes/text_data/Crock Pot Lemon Honey Chicken.txt",
    "content": "Recipe name: Crock Pot Lemon Honey Chicken\n\nNumber of servings: 4\n\nIngredients:\n  1  Roaster Chicken\n  1/2 cup Water\n  2  Lemon\n  1/2 teaspoon Salt\n  1/2 teaspoon Pepper\n  1/4 cup Honey\n\nDirections: Wash the Chicken and trim off excess fat. Put the washed chicken in a crock pot and squeeze the juice from one lemon over the chicken. Then sprinkle the salt and pepper over the chicken, and finally pour the honey over the chicken. Cook the chicken on low heat until done. After serving the chicken, squeeze some juice from the second lemon over each portion.\n"
  },
  {
    "path": "cooking_recipes/text_data/Curried Chicken Legs.txt",
    "content": "Recipe name: Curried Chicken Legs\n\nNumber of servings: 8\n\nIngredients:\n  8  Chicken legs\n  4 teaspoon Curry powder\n  1  Cloves garlic,crushed\n  2 tablespoon Oil\n  1/2 cup Plain yogurt\n  3 tablespoon Lemon juice\n  2 teaspoon Packed brown sugar\n\nDirections: Preheat oven to 400F. Cut at joint into thighs and  drumsticks. You can also but just thighs or the drumbsticks as to your prefenerence, adjusted the recipe. Trim any hanging fat and excess skin from chicken. Heat half of the oil in a small non-stick pan and slowly add in the curry powder, mixing well. Then immediately add the peeled and finely chopped garlic. Cook over medium heat for a few minutes until the garlic softens. Put the contents of the pan into a large bowl, then mixing in yogurt, lemon juice and brownsugar. Add chicken, rolling it around to thoroughly coat. Let this then sit at room temperature for at least 10 minutes. Line a baking sheet with aluminum foil (always with the shiny side up) and use the remaining half of the oil to greese the foil. Put the chicken on the sheet skin side down and cover the chcken with the remaining sauce from the large bowl. Bake for  about 40 minutes. Check chicken for doneness before serving with either rice or pita bread. Optional: serve with Chutney sauce, yogurt, and/or sour cream. Pro tip: make enough for leftovers for lunch the next day!\n"
  },
  {
    "path": "cooking_recipes/text_data/Curried Yogurt Dip.txt",
    "content": "Recipe name: Curried Yogurt Dip\n\nNumber of servings: 6\n\nIngredients:\n  1 cup Plain Yogurt\n  1.5 teaspoon Curry powder\n  1/2 teaspoon Hot pepper sauce\n  1 teaspoon Lemon juice\n  1/2 teaspoon Honey\n  1/4 teaspoon Black pepper\n  1/4 teaspoon Salt\n  3 tablespoon almonds\n\nDirections: Combine ingredients (except almonds!) stirring well. Divide into four small serving bowls. Set aside for about 15 minutes at room temperature before serving. Then, finely chop almonds are sprinkle on top of the yogurt dip. Place the small bowls containing the dip on larger plates with cut up fresh vegetables and/or chips.\n"
  },
  {
    "path": "cooking_recipes/text_data/Dijon Sour Cream Dip.txt",
    "content": "Recipe name: Dijon Sour Cream Dip\n\nNumber of servings: 6\n\nIngredients:\n  1 cup Sour cream OR Plain yogurt\n  1/3 cup Mayonnaise\n  1/4 cup Dijon mustard\n  1 tablespoon Finely-chopped green onion\n  0.25 teaspoon Seasoned salt\n  1/4 teaspoon Black pepper\n\nDirections: Mix all ingredients, cover and store in the refrigerator for at least 30 minutes before serving. Serve dip into small bowls containing and place on larger plates with cut up fresh vegetables and/or chips.\n"
  },
  {
    "path": "cooking_recipes/text_data/Dijon Yogurt Dip.txt",
    "content": "Recipe name: Dijon Yogurt Dip\n\nNumber of servings: 6\n\nIngredients:\n  1 cup Sour cream OR Plain yogurt\n  1/3 cup Mayonnaise\n  1/4 cup Dijon mustard\n  2 tablespoon Finely-chopped green onion\n  0.5 teaspoon Seasoned salt\n  1/4 teaspoon Black pepper\n\nDirections: Mix all ingredients, cover and store in the refrigerator for at least 30 minutes before serving. Serve dip into small bowls containing and place on larger plates with cut up fresh vegetables and/or chips.\n"
  },
  {
    "path": "cooking_recipes/text_data/Easy Cake.txt",
    "content": "Recipe name: Easy Cake\n\nNumber of servings: 8\n\nIngredients:\n  1 cup Butter\n  2 cup Sugar\n  3 cup Flour\n  4  Eggs\n  5 teaspoon Baking powder\n  1 cup Milk\n  1 teaspoon Vanilla extract\n\nDirections: Preheat oven to 350F. Mix together butter, sugar, eggs and vanilla extract in a mixing bowl. Mix in milk and then baking powder. Mix in flour, stirring lightly. Bake for 30  minutes.\n"
  },
  {
    "path": "cooking_recipes/text_data/English Scones.txt",
    "content": "Recipe name: English Scones\n\nNumber of servings: 6\n\nIngredients:\n  2.5 cup Flour\n  1 teaspoon Cream of tartar\n  1 teaspoon Baking soda\n  3 tablespoon Sugar\n  1/2 teaspoon Salt\n  1/2 cup Butter\n  1 cup Milk\n\nDirections: Preheat oven to 400F. Mix flour, cream of tartar, baking soda, sugar, and salt together. Slowly mix in the butter and when all of the butter is mixed in the mix in the milk. Form into three round balls on  floured board and then flatten. Partially cut through each of the three portions to make four pie-like portions. Bake for 15 minutes. Cool for a few minutes then serve immediateley.\n"
  },
  {
    "path": "cooking_recipes/text_data/Fresh Tomato Sauce.txt",
    "content": "Recipe name: Fresh Tomato Sauce\n\nNumber of servings: 4\n\nIngredients:\n  12  Tomatoes\n  2 tablespoon Sugar\n  1 teaspoon black pepper\n  1 teaspoon Salt\n  1 cup Fresh basil\n\nDirections: Place all  ingredients in heavy saucepan or dutch oven and bring to a slow boil. Lower heat  and cook covered for 40 minutes, stirring occasionally. I suggest using a small amount for a meal immediately (with cooked pasta) and freezing most of it for future meals. This is a great recipe to use up extra tomatoes from your garden or when tomatoes are in season and inexpensive to buy.\n"
  },
  {
    "path": "cooking_recipes/text_data/Fried Chicken.txt",
    "content": "Recipe name: Fried Chicken\n\nNumber of servings: 3\n\nIngredients:\n  1  Frying chickens\n  1/4 cup Flour\n  1/2 teaspoon Salt\n  1/2 teaspoon Pepper\n  1/2 teaspoon Cayenne pepper\n  4 tablespoon Butter\n  8 tablespoon Vegetable oil\n\nDirections: Mix flour, salt and pepper in bag, add chicken and shake. Heat oil in a large skillet on high. Brown chicken on skin side. Turn over the chicken, reducing the heat to medium and brown other side. Turn over and cook over lower heat until the chicken is tender when stuck with a fork. Drain off oil and serve.\n"
  },
  {
    "path": "cooking_recipes/text_data/Homemade Salad Dressing.txt",
    "content": "Recipe name: Homemade Salad Dressing\n\nNumber of servings: 16\n\nIngredients:\n  1 teaspoon Salt\n  1/4 teaspoon Black Pepper\n  1/2 teaspoon Paprika\n  1 pinch Dry Mustard\n  1 teaspoon Sugar\n  1/2 cup Olive Oil\n  1/4 cup Wine Vinegar\n  2 clove Clove Garlic\n\nDirections: Dissolve sugar, salt, pepper, paprika, and dry mustard in olive oil. Stir in vinegar. Peel and mince garlic and and add to dressing.\n"
  },
  {
    "path": "cooking_recipes/text_data/Honey Chinese Shrimp.txt",
    "content": "Recipe name: Honey Chinese Shrimp\n\nNumber of servings: 4\n\nIngredients:\n  1/4 cup Honey\n  16  \n  1/4 cup Red wine\n  2 tablespoon Sesame oil\n  1.5 tablespoon Soy Sauce\n  1 teaspoon Worcestershire\n  2 teaspoon Sriracha Hot Chili Sauce\n  1/4 cup Orange juice\n  1/4 teaspoon Black pepper\n\nDirections: Mix together all ingredients and marinate in the refrigerator for at least one hour. There are two ways to cook this dish: 1. Barbecue on a grill, occasionally basting with more marinade. 2. Remove shrimp from marinade sauce and fry in a hot non-stick frying pan. This is good served over brown or white rice with a side of steamed vegetables.\n"
  },
  {
    "path": "cooking_recipes/text_data/Honey-Curried Chicken.txt",
    "content": "Recipe name: Honey-Curried Chicken\n\nNumber of servings: 6\n\nIngredients:\n  2 pound Chicken breasts\n  2 teaspoon Vegetable oil\n  1/4 cup Orange juice\n  1/4 cup Honey\n  1 tablespoon Dijon mustard\n  2 teaspoon Curry powder pinch cayenne pepper\n  1/4 teaspoon cayenne pepper\n\nDirections: preheat oven to 400F Grease a baking dish vegetable oil. Remove skin from chicken. Arrange chicken in a  single layer bone side down if you are not using boneless chicken breasts. Mix the remaining ingredients in a small bowl and pour 2/3 over the chicken. Bake chicken covered for 10 minutes (15 minutes if bones in chicken breasts), basting once. Turn chicken over and baste with the remaining sauce. Bake, now uncovered, for 15 minutes (20 minutes if bones in chicken breasts) longer. Test chicken for doneness and tenderness before serving.\n"
  },
  {
    "path": "cooking_recipes/text_data/Indonesian Barbecue Sauce.txt",
    "content": "Recipe name: Indonesian Barbecue Sauce\n\nNumber of servings: 12\n\nIngredients:\n  1/2 cup Corn syrup (dark)\n  1/4 cup Creamy peanut butter\n  1/4 cup Soy sauce\n  1/4 cup Cider vinegar\n  1/4 cup Sliced green onions\n  4 clove garlic\n  1 teaspoon Clove \n  2 teaspoon Ginger\n  1 teaspoon Crushed dried red pepper\n\nDirections: Mix together all ingredients and let sit for at least one hour before using. Refrigerate or freeze any unused barbecue sauce for future use.\n"
  },
  {
    "path": "cooking_recipes/text_data/Indonesian Chicken Sate.txt",
    "content": "Recipe name: Indonesian Chicken Sate\n\nNumber of servings: 4\n\nIngredients:\n  4  Skinless, boneless chicken breast halves (about 1 1/2 pounds)\n  1/4 cup Reduced-sodium soy sauce\n  1  Fresh lemon juice\n  1 tablespoon Dark molasses\n  1 teaspoon Red Pepper Flakes\n  1 tablespoon Garlic Powder Granules\n  1 tablespoon Ginger\n\nDirections: Cut breasts into long 1 inch strips. Put the chicken and all of the other ingredients into a large bowl and mix well making sure the chicken is well coated. Cover the bowl and refrigerate for two hours or longer. Stir up the bowl a few times while the chicken is marinating. You can use either a (gas or charcoal) grill or the broiler in your oven to cook the Sate. Optional: skewer the chicken strips on wet bamboo skewers - or you can cook the chicken loose if you don't have skewers. Broil or grill  about 4 minutes on each side, occasionally applying more marinade. Optional: if you have any available, serve with Indonesian or Thai peanut sauce, or chutney.\n"
  },
  {
    "path": "cooking_recipes/text_data/Irene's Snickerdoodle Cookies.txt",
    "content": "Recipe name: Irene's Snickerdoodle Cookies\n\nNumber of servings: 10\n\nIngredients:\n  1 cup Shortening\n  1 3/4 cup Sugar\n  2  Eggs\n  3 cup Flour\n  2 teaspoon Cream of tartar\n  1 teaspoon Soda\n  1/4 teaspoon Salt\n  1 dash Cinnamon sugar\n\nDirections: preheat oven to 375F. Mix together the vegetable shortening, sugar and eggs in a small mixing bowl. Sift together flour and mix well with the cream of tartar, soda and salt. Mix together both mixtures and roll into  balls about one inch in diameter and roll in cinnamon and  sugar. Put on a lightly greased baking sheet, leaving about 1 1/2 inches between the cookies. Bake from 10 to 12 minutes until done.\n"
  },
  {
    "path": "cooking_recipes/text_data/Japanese Ginger Salad Dressing.txt",
    "content": "Recipe name: Japanese Ginger Salad Dressing\n\nNumber of servings: 4\n\nIngredients:\n  1 tablespoon Rice vinegar\n  2 teaspoon Vegetable oil\n  1.5 tablespoon Sesame oil\n  1 tablespoon Grated fresh ginger\n  1 teaspoon Soy sauce\n\nDirections: Mix all ingredients together and let sit for at least 10 minutes before using. Some of the ingredients will settle so make sure you stir well before pouring over salad.\n"
  },
  {
    "path": "cooking_recipes/text_data/Lemon Sesame Broccoli.txt",
    "content": "Recipe name: Lemon Sesame Broccoli\n\nNumber of servings: 4\n\nIngredients:\n  2 cup Broccoli\n  2 teaspoon Sesame oil\n  1/2 tablespoon fresh lemon juice\n  1/4 teaspoon black pepper\n  1 teaspoon Salt\n\nDirections: Cut flowerets off of the broccoli and cut into small pieces. Cut the stems into very thin round pieces so they will cook quickly. Put the broccoli stems into  boiling water in saucepan, cover and cook for 3 minutes. Add flowerets, cover and cook 5 minutes, and then drain all broccoli and place in a serving bowl. Mix broccoli well with the sesame oil, lemon juice, salt, and black pepper. Eat immediately\n"
  },
  {
    "path": "cooking_recipes/text_data/Poached Salmon with Wine and Lemon.txt",
    "content": "Recipe name: Poached Salmon with Wine and Lemon\n\nNumber of servings: 4\n\nIngredients:\n  1/4 cup Lemon juice\n  1/4 cup Dry white wine\n  4  Salmon fillets\n  1/4 teaspoon Salt\n  1/4 teaspoon Pepper\n  1/4 cup Water\n\nDirections: Use a heavy frying pan with a cover or small dutch oven to make this. Over medium heat bring the lemon juice and wine to a boil, adding in the salt and pepper, then water. Add salmon 'bad looking side' down and cover the pan. Poach salmon for 7 or 8 minutes.  The fish should easily flake when it is done. Remove from the pan and eat immediately.\n"
  },
  {
    "path": "cooking_recipes/text_data/Quick Sweet and Sour Sauce.txt",
    "content": "Recipe name: Quick Sweet and Sour Sauce\n\nNumber of servings: 8\n\nIngredients:\n  1 cup White vinegar\n  2/3 cup Sugar\n  2 tablespoon Ketchup\n  2 tablespoon fresh ginger\n  1/4 teaspoon Salt\n  1/4 teaspoon Tabasco sauce\n\nDirections: Use a medium size pan to combine the vinegar, sugar, ketchup, ginger, salt and tabasco sauce and bring to a boil under high heat. Cook until the sauce is reduced to a fairly thick sauce. You don't want the sauce to be runny but you also don't want it to be too thick. Pay attention while cooking the sauce!\n"
  },
  {
    "path": "cooking_recipes/text_data/Scottish Shortbread.txt",
    "content": "Recipe name: Scottish Shortbread\n\nNumber of servings: 10\n\nIngredients:\n  4 cup Plain flour\n  2 cup Butter (nothing else will do)\n  1/2 cup Sugar\n  1 dash Salt\n\nDirections: Preheat oven to 350F Sift flour on a large board. Place the sugar in a separate area on the board and mix in the butter. Knead in flour adding small amounts at a time. Grease a baking sheet. Roll dough into small balls, about one inch in diameter. Flatten the balls and place on the baking sheet. Bake for 30 minutes.\n"
  },
  {
    "path": "cooking_recipes/text_data/Simple Italian Chicken.txt",
    "content": "Recipe name: Simple Italian Chicken\n\nNumber of servings: 4\n\nIngredients:\n  2 lb Skinned chicken breasts\n  2 tablespoon Olive oil\n  2 tablespoon Lemon juice\n  2  Cloves garlic,crushed\n  1/2 teaspoon Oregano\n  4 tablespoon Dry white wine\n\nDirections: Preheat oven to 375F. Grease a deep baking pan with half of the olive oil. Mix all remaining ingredients except the chicken in the baking dish and stir together. Wash the chicken and place in the baking dish, rolling the chicken to coat it with the mixture. Cover the baking dish and cook for 45 minutes. Optional: uncover the baking dish the last 15 minutes. Test the chicken for doneness and tenderness before serving.\n"
  },
  {
    "path": "cooking_recipes/text_data/Spicy Barbecued Chicken.txt",
    "content": "Recipe name: Spicy Barbecued Chicken\n\nNumber of servings: 4\n\nIngredients:\n  1  chicken (2 lb)\n  1  Juice of 1 lemon\n  2  Cloves garlic\n  2 teaspoon Fresh ground black pepper\n  1 teaspoon Salt\n  2 teaspoon Ground cayenne pepper\n  1 teaspoon Paprika\n  2 tablespoon Melted butter\n\nDirections: Cut as much extra fat and loose skin as you can from the chicken. Later when you are grilling it this will help to minimize grease flames burning the chicken. Split the chicken by cutting down the backbone and opening it to leave the breast attached - flatten out as much as possible while still keeping the chicken in one piece. Squeeze the juice from the lemon on to the  chicken. Mix together the cayenne pepper, black pepper, salt, peeled and crushed garlic, paprika and add to the melted butter; pur and rub this mixture over the chicken. Refrigerate the chicken (it can be uncovered) for at least an hour or two to let the spices set. Prepare a gas grill or charcoal grill. For a gas grill heat it to medium temperature. For a charcoal grill, use a modest amount of charcoal to avoid too high of cooking heat. Put the  chicken breast side up onto the grill and cover the grill with the lid. Cook chicken for 30 minutes without turning it. Turn the chicken and cook uncovered for about another 5 to 10 minutes to crisp up the skin. Remove the chicken from the grill, cut into a thick area (thigh or breast) to make sure it is done, let it cool for 5 minutes and then serve. Note: if you substitute chicken parts like legs or breasts then the cooking time will be reduced; keep an eye on the chicken and test for doneness before serving.\n"
  },
  {
    "path": "cooking_recipes/text_data/Spicy Braised Chicken.txt",
    "content": "Recipe name: Spicy Braised Chicken\n\nNumber of servings: 4\n\nIngredients:\n  1 teaspoon Red peppers\n  1 tablespoon Oil\n  1 teaspoon Salt\n  1 pound Chicken meat\n  1 tablespoon Fresh ginger\n  1 tablespoon Brown sugar\n  2 teaspoon Sherry\n  1 teaspoon Cornstarch\n  2 teaspoon Soy sauce\n\nDirections: Using a small bowl, mix the cornstarch to a smooth paste with the soy sauce and set aside. Cut up the chicken into small bite-size pieces. Over medium high heat add oil and fry the chicken and minced fresh ginger for 1 minute. Add in the mixed cornstarch and soy sauce and cook for another minute, stiring often. Add the sugar and sherry and cook for another minute. Test the chicken for doneness and then serve.\n"
  },
  {
    "path": "cooking_recipes/text_data/Tasty Black Bean Dip.txt",
    "content": "Recipe name: Tasty Black Bean Dip\n\nNumber of servings: 4\n\nIngredients:\n  1.5 cup 16-0z can black beans\n  2 tablespoon Chopped red onion\n  1 tablespoon Balsamic vinegar\n  1  Clove garlic\n  1/2 cup Olive Oil\n\nDirections: You can use canned black beans (each 12 ounce can is about 1 1/2 cups) or cook dry beans according to package directions and then cool. Combine ingredients in the blender or food processor and mix for about 1 minute to either a fine or course consistency depending on your tastes and preferences. I like to serve this in small individual bowls place on a larger plate so there is plenty of room on each plate for pita bread, chips, and/or cut up raw vegetables.\n"
  },
  {
    "path": "cooking_recipes/text_data/Whole Wheat Bread.txt",
    "content": "Recipe name: Whole Wheat Bread\n\nNumber of servings: 10\n\nIngredients:\n  1 1/2 cup Water\n  1 1/3 tablespoon Vegetable oil\n  1/4 cup Honey\n  1/2 teaspoon Salt\n  4 cup Whole wheat bread flour\n  3 teaspoon Yeast\n\nDirections: Lightly grease two 9x5 inch loaf pans. Blend all ingredients and then place on a large floured surface and knead for 5 or 6 minutes. Place in a covered bowl in a warm area and let it rise for about one hour. Preheat oven to 375F. Bake for 35 to 40 minutes.\n"
  },
  {
    "path": "data/chemistry.txt",
    "content": "Amyl alcohol is an organic compound with the formula C 5 H 12 O. All eight isomers of amyl alcohol are known. The most important is isobutyl carbinol, this being the chief constituent of fermentation amyl alcohol, and consequently a constituent of fusel oil. It can be separated from fusel oil by shaking with strong brine , separating the oily layer from the brine layer and it, the portion boiling between 125 and 140 °C. being collected. For further purification it may be shaken with hot lime water, the oily layer separated, dried with calcium chloride and fractionated, the fraction boiling between 128 and 132 °C only being collected.\nThe 1730 definition of the word \"chemistry\", as used by Georg Ernst Stahl, meant the art of resolving mixed, compound, or aggregate bodies into their principles; and of composing such bodies from those principles.[15] In 1837, Jean-Baptiste Dumas considered the word \"chemistry\" to refer to the science concerned with the laws and effects of molecular forces.[16] This definition further evolved until, in 1947, it came to mean the science of substances: their structure, their properties, and the reactions that change them into other substances - a characterization accepted by Linus Pauling.[17] More recently, in 1998, the definition of \"chemistry\" was broadened to mean the study of matter and the changes it undergoes, as phrased by Professor Raymond Chang.\nThe current model of atomic structure is the quantum mechanical model.[36] Traditional chemistry starts with the study of elementary particles, atoms, molecules,[37] substances, metals, crystals and other aggregates of matter. This matter can be studied in solid, liquid, or gas states, in isolation or in combination. The interactions, reactions and transformations that are studied in chemistry are usually the result of interactions between atoms, leading to rearrangements of the chemical bonds which hold atoms together. Such behaviors are studied in a chemistry laboratory.\n\nThe chemistry laboratory stereotypically uses various forms of laboratory glassware. However glassware is not central to chemistry, and a great deal of experimental (as well as applied/industrial) chemistry is done without it.\nThe transfer of energy from one chemical substance to another depends on the size of energy quanta emitted from one substance. However, heat energy is often transferred more easily from almost any substance to another because the phonons responsible for vibrational and rotational energy levels in a substance have much less energy than photons invoked for the electronic energy transfer. Thus, because vibrational and rotational energy levels are more closely spaced than electronic energy levels, heat is more easily transferred between substances relative to light or other forms of electronic energy. For example, ultraviolet electromagnetic radiation is not transferred with as much efficacy from one substance to another as thermal or electrical energy.\n"
  },
  {
    "path": "data/economics.txt",
    "content": "The Austrian School (also known as the Vienna School or the Psychological School ) is a Schools of economic thought|school of economic thought that emphasizes the spontaneous organizing power of the price mechanism. Austrians hold that the complexity of subjective human choices makes mathematical modelling of the evolving market extremely difficult (or Undecidable and advocate a \"laissez faire\" approach to the economy. Austrian School economists advocate the strict enforcement of voluntary contractual agreements between economic agents, and hold that commercial transactions should be subject to the smallest possible imposition of forces they consider to be (in particular the smallest possible amount of government intervention). The Austrian School derives its name from its predominantly Austrian founders and early supporters, including Carl Menger, Eugen von Böhm-Bawerk and Ludwig von Mises.\n\nEconomics is the social science that analyzes the production, distribution, and consumption of goods and services.  Political economy was the earlier name for the subject, but economists in the late 19th century suggested \"economics\" as a shorter term for \"economic science\" that also avoided a narrow political-interest connotation and as similar in form to \"mathematics\", \"ethics\", and so forth.[2]\n\nA focus of the subject is how economic agents behave or interact and how economies work. Consistent with this, a primary textbook distinction is between microeconomics and macroeconomics. Microeconomics examines the behavior of basic elements in the economy, including individual agents (such as households and firms or as buyers and sellers) and markets, and their interactions. Macroeconomics analyzes the entire economy and issues affecting it, including unemployment, inflation, economic growth, and monetary and fiscal policy.\n\n\t\tThe professionalization of economics, reflected in the growth of graduate programs on the subject, has been described as \"the main change in economics since around 1900\".[93] Most major universities and many colleges have a major, school, or department in which academic degrees are awarded in the subject, whether in the liberal arts, business, or for professional study; see Master of Economics.\n\n\t\t\n\t\tEconomics is the social science that studies the behavior of individuals, households, and organizations (called economic actors, players, or agents), when they manage or use scarce resources, which have alternative uses, to achieve desired ends. Agents are assumed to act rationally, have multiple desirable ends in sight, limited resources to obtain these ends, a set of stable preferences, a definite overall guiding objective, and the capability of making a choice. There exists an economic problem, subject to study by economic science, when a decision (choice) is made by one or more resource-controlling players to attain the best possible outcome under bounded rational conditions. In other words, resource-controlling agents maximize value subject to the constraints imposed by the information the agents have, their cognitive limitations, and the finite amount of time they have to make and execute a decision. Economic science centers on the activities of the economic agents that comprise society.[1] They are the focus of economic analysis.[2]\n\n\t\tThe traditional concern of economic analysis is to gain an understanding of the processes that govern the production, distribution and consumption of goods and services in an exchange economy.[3] An approach to understanding these processes, through the study of agent behavior under scarcity, may go as   s:\n\nAn interesting Economist is Pauli Blendergast who teaches at the University of Krampton Ohio and is famouse for saying economics is bullshit."
  },
  {
    "path": "data/health.txt",
    "content": "    \r\n    which requires that you sit at a desk all day.  ; If you hate to talk\r\n     politics, don't associate with people who love to talk politics, etc.   Learn to live one day at a time.   Every day, do something you really enjoy.   Add an ounce of love to everything you do.   Take a hot bath or shower (or a cool one in summertime) to relieve tension.   Do something for somebody else.   Focus on understanding rather than on being understood; on loving rather than on being loved.   Do something that will improve your appearance.  ; Looking better can help you feel better.   Schedule a realistic day.  ; Avoid the tendency to schedule back-to-back appointments; allow time between appointments for a breathing spell.   Become more flexible.  ; Some things are worth not doing perfectly and\r\n some issues are fine to compromise upon.   Eliminate destructive self-talking\r\n\r\n I also felt they protected me from the hard road by interposing a layer of air between the sole of my foot and the pavement.   So why was I sidelined with a heel injury for over two   s?  I listened to the manufacturer and changed my runners every 400 miles.  Come to think of it, why do I see so many runners with lower extremity injuries in my office?  The traditional answer to these questions has always been overuse often compounded by an underlying mechanical abnormality such as over-pronation or flat-feet.  The treatment, along with modification of training, physiotherapy, stretching etc. has always included a close look at the runner's footwear, often with recommendations about motion control, stability, cushioning, orthotics or custom molded insoles.\r\n I do not recommend that you run your next half-marathon barefoot.  But certainly, I predict that sooner or later, changes will come about in both shoe design and training.  From the medical establishment's point of view, the prevention and treatment of running injuries must change to incorporate the concepts outlined above.  In fact I view the ideas I've presented here as a major paradigm shift in sports medicine, the likes of which I have not seen in the last fifteen years.  Of course, the major shoe companies have to own up and start introducing better shoes into their lines.\r\n\r\n\t\tadaptive immunity: The ability of the body to learn to fight specific infections after being exposed to the germs that cause them.\r\n\r\n\t\taddiction: Loss of control over indulging in a substance or performing an action or behavior, and continued craving for it despite negative consequences.\r\n\r\n\t\t\r\n\t\tadenosine triphosphate: An energy-storing molecule that is found in all human cells. Usually abbreviated as ATP.\r\n\r\n\t\tadequate intake: An    of the amount of a nutrient needed by healthy people. The Adequate Intake is used when there isn’t enough information to set a recommended dietary allowance (RDA).\r\n\r\n\t\tagoraphobia: Fear and avoidance of public places and open spaces.\r\n\r\n\t\tamnesia: Unusual memory loss or forgetfulness.\r\n\r\n\t\tamputation: The surgical removal of a limb or other body part.\r\n\r\n\t\tanaerobic: Any process that doesn’t require oxygen. Often refers to a form of short, high intensity exercise, known as anaerobic exercise.\r\n\r\n\t\tanaerobic exercise: Exercise that improves the efficiency of energy-producing systems that do not rely on oxygen. Examples include sprinting and weight lifting.\r\n\r\n\t\t\r\n\t\tdry eye: Stinging, burning, or irritation that occurs when the eye doesn’t produce enough moisture.\r\n\r\n\t\tduct: A tube or vessel in the body which carries the secretion of a gland; Secretion examples are tears, breast milk, etc.\r\n\r\n\r\n\t\tupper airway resistance syndrome: Inhalation that requires undue extra exertion; this extra work may cause insomnia and daytime sleepiness.\r\n\r\n\t\turea: A waste product of protein digestion and metabolism.\r\n\r\n\t\tureter: The tube that connects each kidney to the bladder.\r\n\r\n\t\turethra: The tube leading from the bladder through which urine is carried from the body.\r\n"
  },
  {
    "path": "data/sports.txt",
    "content": "Sport is generally recognised as activities based in physical athleticism or physical dexterity.[3] Sports are usually governed by rules to ensure fair competition and consistent adjudication of the winner.\n\n\"Sport\" comes from the Old French desport meaning \"leisure\", with the oldest definition in English from around 1300 being \"anything humans find amusing or entertaining\".[4]\n\nOther bodies advocate widening the definition of sport to include all physical activity and exercise. For instance, the Council of Europe include all forms of physical exercise, including those completed just for fun.\n\n"
  },
  {
    "path": "data_small/sports.txt",
    "content": "Sport is generally recognised as activities based in physical athleticism or physical dexterity.[3] Sports are usually governed by rules to ensure fair competition and consistent adjudication of the winner.\n\n\"Sport\" comes from the Old French desport meaning \"leisure\", with the oldest definition in English from around 1300 being \"anything humans find amusing or entertaining\".[4]\n\nOther bodies advocate widening the definition of sport to include all physical activity and exercise. For instance, the Council of Europe include all forms of physical exercise, including those completed just for fun.\n\n"
  },
  {
    "path": "embedchain_test/.gitignore",
    "content": "db\n\n"
  },
  {
    "path": "embedchain_test/README.md",
    "content": "# Creating an index\n\n    python process_pdfs.py\n\nI hardwired the directory path for PDF files for my books in the file **process_pdfs.py** - change that for your system.\n\n# Querying the processed PDF files\n\n```\n$ python app.py\nHow can I iterate over a list in Haskell?\nTo iterate over a list in Haskell, you can use recursion or higher-order functions like `map` or `foldl`. \n\nHow can I edit my Common Lisp files?\nTo edit Common Lisp files, you can use Emacs with the Lisp editing mode. By setting the default auto-mode-alist in Emacs, whenever you open a file with the extensions \".lisp\", \".lsp\", or \".cl\", Emacs will automatically use the Lisp editing mode. You can search for an \"Emacs tutorial\" online to learn how to use the basic Emacs editing commands. \n\nHow can I scrape a website using Common Lisp?\nOne way to scrape a website using Common Lisp is to use the Drakma library. Paul Nathan has written a library using Drakma called web-trotter.lisp, which is available under the AGPL license at articulate-lisp.com/src/web-trotter.lisp. This library can be a good starting point for your scraping project. Additionally, you can use the wget utility to make local copies of a website. The command \"wget -m -w 2 http:/knowledgebooks.com/\" can be used to mirror a site with a two-second delay between HTTP requests for resources. The option \"-m\" indicates to recursively follow all links on the website, and the option \"-w 2\" adds a two-second delay between requests. Another option, \"wget -mk -w 2 http:/knowledgebooks.com/\", converts URI references to local file references on your local mirror. Concatenating all web pages into one file can also be a useful trick. \n```"
  },
  {
    "path": "embedchain_test/app.py",
    "content": "\n# https://github.com/embedchain/embedchain\n\nfrom embedchain import App\n\ntest_chat = App()\n\n\ndef test(q):\n    print(q)\n    print(test_chat.query(q), \"\\n\")\n\n#test(\"What is the Blackboard Metaphor?\")\ntest(\"How can I iterate over a list in Haskell?\")\ntest(\"How can I edit my Common Lisp files?\")\ntest(\"How can I scrape a website using Common Lisp?\")"
  },
  {
    "path": "embedchain_test/data/haskell-cookbook.txt",
    "content": "\nHaskell Tutorial and Cookbook\nMark Watson\nThis book is for sale at http://leanpub.com/haskell-cookbook\nThis version was published on 2023-01-27\nThis is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing\nprocess. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and\nmany iterations to get reader feedback, pivot until you have the right book and build traction once\nyou do.\n© 2016 - 2023 Mark Watson\n\nContents\nCover Material, Copyright, and License . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n1\nPreface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n2\nAdditional Material in the Second Edition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n2\nA Request from the Author . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n2\nStructure of the Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n3\nCode Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n4\nFunctional Programming Requires a Different Mind Set . . . . . . . . . . . . . . . . . . . . .\n4\neBooks Are Living Documents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n4\nSetting Up Your Development Environment\n. . . . . . . . . . . . . . . . . . . . . . . . . . . .\n5\nWhy Haskell? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n6\nEnjoy Yourself . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n7\nAcknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n7\nSection 1 - Tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n8\nTutorial on Pure Haskell Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n9\nInteractive GHCi Shell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n9\nIntroduction to Haskell Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n17\nFunctions Are Pure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n21\nUsing Parenthesis or the Special $ Character and Operator Precedence . . . . . . . . . . . .\n23\nLazy Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n25\nUnderstanding List Comprehensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n26\nHaskell Rules for Indenting Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n28\nUnderstanding let and where . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n29\nConditional do Expressions and Anonymous Functions . . . . . . . . . . . . . . . . . . . . .\n30\nMaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n36\nSets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n37\nMore on Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n38\nComments on Dealing With Immutable Data and How to Structure Programs . . . . . . .\n40\nError Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n41\nTesting Haskell Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n42\nPure Haskell Wrap Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n45\nTutorial on Impure Haskell Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n46\n\nCONTENTS\nHello IO () Monad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n46\nA Note About >> and >>= Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n49\nConsole IO Example with Stack Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . .\n51\nFile IO\n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n54\nError Handling in Impure Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n56\nNetwork IO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n58\nA Haskell Game Loop that Maintains State Functionally . . . . . . . . . . . . . . . . . . . . .\n61\nA More Detailed Look at Monads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n63\nUsing Applicative Operators <$> and <*>: Finding Common Words in Files . . . . . . . . .\n65\nList Comprehensions Using the do Notation . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n68\nDealing With Time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n69\nUsing Debug.Trace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n70\nWrap Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n72\nSection 2 - Cookbook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n73\nText Processing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n74\nCSV Spreadsheet Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n74\nJSON Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n76\nCleaning Natural Language Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n78\nNatural Language Processing Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n81\nResolve Entities in Text to DBPedia URIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n82\nBag of Words Classification Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n87\nText Summarization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n92\nPart of Speech Tagging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n94\nNatural Language Processing Wrap Up\n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n98\nLinked Data and the Semantic Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n99\nThe SPARQL Query Language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100\nA Haskell HTTP Based SPARQL Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101\nQuerying Remote SPARQL Endpoints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103\nLinked Data and Semantic Web Wrap Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106\nWeb Scraping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107\nUsing the Wreq Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107\nUsing the HandsomeSoup Library for Parsing HTML . . . . . . . . . . . . . . . . . . . . . . 111\nWeb Scraping Wrap Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113\nUsing Relational Databases\n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114\nDatabase Access for Sqlite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114\nDatabase Access for Postgres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115\nHaskell Program to Play the Blackjack Card Game . . . . . . . . . . . . . . . . . . . . . . . . . 120\n\nCONTENTS\nSection 3 - Larger Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131\nKnowledge Graph Creator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132\nCode Layout For the KGCreator Project and strategies for sharing Haskell code between\nprojects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134\nThe Main Event: Detecting Entities in Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136\nUtility Code for Generating RDF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138\nUtility Code for Generating Cypher Input Data for Neo4J . . . . . . . . . . . . . . . . . . . . 145\nTop Level API Code for Handling Knowledge Graph Data Generation . . . . . . . . . . . . 150\nWrapup for Automating the Creation of Knowledge Graphs\n. . . . . . . . . . . . . . . . . . 152\nHybrid Haskell and Python Natural Language Processing . . . . . . . . . . . . . . . . . . . . . 153\nExample Use of the Haskell NLP Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153\nSetting up the Python NLP Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153\nUnderstanding the Haskell NLP Client Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154\nWrapup for Using the Python SpaCy NLP Service . . . . . . . . . . . . . . . . . . . . . . . . . 156\nHybrid Haskell and Python For Coreference Resolution . . . . . . . . . . . . . . . . . . . . . . 157\nInstalling the Python Coreference Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157\nUnderstanding the Haskell Coreference Client Code . . . . . . . . . . . . . . . . . . . . . . . 158\nWrapup for Using the Python Coreference NLP Service . . . . . . . . . . . . . . . . . . . . . 160\nBook Wrap Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161\nAppendix A - Haskell Tools Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162\nstack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162\nEmacs Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163\nDo you want more of an IDE-like Development Environment? . . . . . . . . . . . . . . . . . 163\nhlint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163\n\nCover Material, Copyright, and\nLicense\nCopyright 2016 Mark Watson. All rights reserved. This book may be shared using the Creative\nCommons “share and share alike, no modifications, no commercial reuse” license.\nThis eBook will be updated occasionally so please periodically check the leanpub.com web page for\nthis book¹ for updates.\nPlease visit the author’s website².\nIf you found a copy of this book on the web and find it of value then please consider buying a copy\nat leanpub.com/haskell-cookbook³ to support the author and fund work for future updates.\n¹https://leanpub.com/haskell-cookbook\n²http://markwatson.com\n³https://leanpub.com/haskell-cookbook\n\nPreface\nThis is the preface to the new second edition released summer of 2019.\nIt took me over a year learning Haskell before I became comfortable with the language because I tried\nto learn too much at once. There are two aspects to Haskell development: writing pure functional\ncode and writing impure code that needs to maintain state and generally deal with the world non-\ndeterministically. I usually find writing pure functional Haskell code to be easy and a lot of fun.\nWriting impure code is sometimes a different story. This is why I am taking a different approach\nto teaching you to program in Haskell: we begin techniques for writing concise, easy to read and\nunderstand efficient pure Haskell code. I will then show you patterns for writing impure code to\ndeal with file IO, network IO, database access, and web access. You will see that the impure code\ntends to be (hopefully!) a small part of your application and is isolated in the impure main program\nand in a few impure helper functions used by the main program. Finally, we will look at a few larger\nHaskell programs.\nAdditional Material in the Second Edition\nIn addition to updating the introduction to Haskell and tutorial material, I have added a few larger\nprojects to the second edition.\nThe project knowledge_graph_creator helps to automate the process of creating Knowledge\nGraphs from raw text input and generates data for both the Neo4J open source graph database\nas well as RDF data for use in semantic web and linked data applications.\nThe project HybridHaskellPythonNlp is a hybrid project: a Python web service that provides access\nto the SpaCy natural language processing (NLP) library and select NLP deep learning models and a\nHaskell client for accessing this service. It sometimes makes sense to develop polyglot applications\n(i.e., applications written in multiple programming languages) to take advantage of language specific\nlibraries and frameworks. We will also use a similar hybrid example HybridHaskellPythonCore-\nfAnaphoraResolution that uses another deep learning model to replace pronouns in text with the\noriginal nouns that the pronouns refer to. This is a common processing step for systems that extract\ninformation from text.\nA Request from the Author\nI spent time writing this book to help you, dear reader. I release this book under the Creative\nCommons “share and share alike, no modifications, no commercial reuse” license and set the\nminimum purchase price to $6.00 in order to reach the most readers. You can also read this (and\n\nPreface\n3\nall of my books) for free on my my website⁴. Under this license you can share a PDF version of this\nbook with your friends and coworkers.\nIf you would like to support my work please consider purchasing my books on Leanpub⁵ and star\nmy git repositories that you find useful on GitHub⁶. You can also interact with me on social media\non Mastodon⁷ and Twitter⁸.\nI enjoy writing and your support helps me write new editions and updates for my books and to\ndevelop new book projects. Thank you!\nStructure of the Book\nThe first section of this book contains two chapters:\n• A tutorial on pure Haskell development: no side effects.\n• A tutorial on impure Haskell development: dealing with the world (I/O, network access,\ndatabase access, etc.). This includes examples of file IO and network programming as well\nas writing short applications: a mixture of pure and impure Haskell code.\nAfter working through these tutorial chapters you will understand enough of Haskell development\nto understand and be able to make modifications for your own use of the cookbook examples in\nthe second section. Some of the general topics will be covered again in the second book section\nthat contains longer sample applications. For example, you will learn the basics for interacting with\nSqlite and Postgres databases in the tutorial on impure Haskell code but you will see a much longer\nexample later in the book when I provide code that implements a natural language processing (NLP)\ninterface to relational databases.\nThe second section contains the following recipes implemented as complete programs:\n• Textprocessing CSV Files\n• Textprocessing JSON Files\n• Natural Language Processing (NLP) interface to relational databases, including annotating\nEnglish text with Wikipedia/DBPedia URIs for entities in the original text. Entities can be\npeople, places, organizations, etc.\n• Accessing and Using Linked Data\n• Querying Semantic Web RDF Data Sources\n• Web scraping data on web sites\n• Using Sqlite and Postgres relational databases\n• Play a simple version of Blackjack card game\nA new third section (added in 2019 for the second edition) has three examples that were derived by\nmy own work.\n⁴https://markwatson.com/books\n⁵https://leanpub.com/u/markwatson\n⁶https://github.com/mark-watson?tab=repositories&q=&type=public\n⁷https://mastodon.social/@mark_watson\n⁸https://twitter.com/mark_l_watson\n\nPreface\n4\nCode Examples\nThe code examples in this book are licensed under two software licenses and you can choose the\nlicense that works best for your needs: Apache 2 and GPL 3. To be clear, you can use the examples in\ncommercial projects under the Apache 2 license and if you like to write Free (Libre) software then\nuse the GPL 3 license.\nWe will use stack as a build system for all code examples. The code examples are provided as 22\nseparate stack based projects. These examples are found on github⁹.\nFunctional Programming Requires a Different Mind Set\nYou will learn to look at problems differently when you write functional programs. We will use\na bottom up approach in most of the examples in this book. I like to start by thinking of the\nproblem domain and decide how I can represent the data required for the problem at hand. I prefer\nto use native data structures. This is the opposite approach to object oriented development where\nconsiderable analysis effort and coding effort is required to define class hierachies to represent data.\nIn most of the code we use simple native data types like lists and maps.\nOnce we decide how to represent data for a program we then start designing and implementing\nsimple functions to operate on and transform data. If we find ourselves writing functions that are\ntoo long or too complex, we can break up code into simpler functions. Haskell has good language\nsupport for composing simple functions into more complex operations.\nI have spent many years engaged in object oriented programming starting with CLOS for Common\nLisp, C++, Java, and Ruby. I now believe that in general, and I know it is sometimes a bad\nidea to generalize too much, functional programming is a superior paradigm to object oriented\nprogramming. Convincing you of this belief is one of my goals in writing this book!\neBooks Are Living Documents\nI wrote printed books for publishers like Springer-Verlag, McGraw-Hill, and Morgan Kaufman before\nI started self-publishing my own books. I prefer eBooks because I can update already published books\nand update the code examples for eBooks.\nI encourage you to periodically check for free updates to both this book and the code examples on\nthe leanpub.com web page for this book¹⁰.\n⁹https://github.com/mark-watson/haskell_tutorial_cookbook_examples\n¹⁰https://leanpub.com/haskell-cookbook\n\nPreface\n5\nSetting Up Your Development Environment\nI strongly recommend that you use the stack tool from the stack website¹¹. This web site has\ninstructions for installing stack on OS X, Windows, and Linux. If you don’t have stack installed yet\nplease do so now and follow the “getting started” instructions for creating a small project. Appendix\nA contains material to help get you set up.\nIt is important for you to learn the basics of using stack before jumping into this book because I\nhave set up all of the example programs using stack.\nThe github repository for the examples in this book is located at github.com/mark-watson/haskell_-\ntutorial_cookbook_examples¹².\nMany of the example listings for code examples are partial or full listing of files in my github\nrepository. I show the file name, the listing, and the output. To experiment with the example yourself\nyou need to load it and execute the main function; for example, if the example file is TestSqLite1.hs\nin the sub-directory Database, then from the top level directory in the git repository for the book\nexamples you would do the following:\n$ haskell_tutorial_cookbook_examples git:(master) > cd Database\n$ Database git:(master) > stack build --exec ghci\nGHCi, version 7.10.3: http://www.haskell.org/ghc/\n:? for help\nPrelude> :l TestSqLite1\n[1 of 1] Compiling Main\n( TestSqLite1.hs, interpreted )\nOk, modules loaded: Main.\n*Main> main\n\"Table names in database test.db:\"\n\"test\"\n\"SQL to create table 'test' in database test.db:\"\n\"CREATE TABLE test (id integer primary key, str text)\"\n\"number of rows in table 'test':\"\n1\n\"rows in table 'test':\"\n(1,\"test string 2\")\n*Main>\nIf you don’t want to run the example in a REPL in order to experiment with it interactively you can\nthen just run it via stack using:\n¹¹http://docs.haskellstack.org/en/stable/README.html\n¹²https://github.com/mark-watson/haskell_tutorial_cookbook_examples\n\nPreface\n6\n$ Database git:(master) > stack build --exec TestSqlite1\n\"Table names in database test.db:\"\n\"test\"\n\"SQL to create table 'test' in database test.db:\"\n\"CREATE TABLE test (id integer primary key, str text)\"\n\"number of rows in table 'test':\"\n1\n\"rows in table 'test':\"\n(1,\"test string 2\")\nI include README.md files in the project directories with specific instructions.\nI now use VSCode for most of my Haskell development. With the Haskell plugins VSCode offers\nauto-completion while typing and highlights syntax errors. Previously I use other editor for Haskell\ndevelopment. If you are an Emacs user I recommend that you follow the instructions in Appendix\nA, load the tutorial files into an Emacs buffer, build an example and open a REPL frame. If one is not\nalready open type control-c control-l, switch to the REPL frame, and run the main function. When\nyou make changes to the tutorial files, doing another control-c control-l will re-build the example\nin less than a second. In addition to using Emacs I occasionally use the IntelliJ Community Edition\n(free) IDE with the Haskell plugin, the TextMate editor (OS X only) with the Haskell plugin, or the\nGNU GEdit editor (Linux only).\nAppendix A also shows you how to setup the *stack Haskell build tool.\nWhether you use Emacs/VSCode or run a REPL in a terminal window (command window if you are\nusing Windows) the important thing is to get used to and enjoy the interactive style of development\nthat Haskell provides.\nWhy Haskell?\nI have been using Lisp programming languages professionally since 1982. Lisp languages are flexible\nand appropriate for many problems. Some might dissagree with me but I find that Haskell has most\nof the advantages of Lisp with the added benefit of being strongly typed. Both Lisp and Haskell\nsupport a style of development using an interactive shell (or “repl”).\nWhat does being a strongly typed language mean? In a practical sense it means that you will often\nencounter syntax errors caused by type mismatches that you will need to fix before your code will\ncompile (or run in the GHCi shell interpreter). Once your code compiles it will likely work, barring\na logic error. The other benefit that you can get is having to write fewer unit tests - at least that is\nmy experience. So, using a strongly typed language is a tradeoff. When I don’t use Haskell I tend to\nuse dynamic languages like Common Lisp or Python.\n\nPreface\n7\nEnjoy Yourself\nI have worked hard to make learning Haskell as easy as possible for you. If you are new to the\nHaskell programming language then I have something to ask of you, dear reader: please don’t rush\nthrough this book, rather take it slow and take time to experiment with the programming examples\nthat most interest you.\nAcknowledgements\nI would like to thank my wife Carol Watson for editing the manuscript for this book. I would like\nto thank Roy Marantz, Michel Benard, and Daniel Kroni for reporting an errors.\n\nSection 1 - Tutorial\nThe first section of this book contains two chapters:\n• A tutorial on pure Haskell development: no side effects.\n• A tutorial on impure Haskell development: dealing with the world (I/O, network access,\ndatabase access, etc.)\nAfter working through these two tutorial chapters you will have sufficient knowledge of Haskell\ndevelopment to understand the cookbook examples in the second section and be able to modify\nthem for your own use. Some of the general topics will be covered again in the second book section\nthat contains longer example programs.\n\nTutorial on Pure Haskell\nProgramming\nPure Haskell code has no side effects and if written properly is easy to read and understand. I am\nassuming that you have installed stack using the directions in Appendix A. It is important to keep\na Haskell interactive repl open as you read the material in this book and experiment with the code\nexamples as you read. I don’t believe that you will be able to learn the material in this chapter unless\nyou work along trying the examples and experimenting with them in an open Haskell repl!\nThe directory Pure in the git repository contains the examples for this chapter. Many of the examples\ncontain a small bit of impure code in a main function. We will cover how this impure code works\nin the next chapter but let’s look at a short example of impure code that is contained inside a main\nfunction:\nmain = do\nputStrLn (\"1 + 2 = \" ++ show (1 + 2))\nThe function main is the entry point of this short two line program. When the program is run, the\nmain function will be executed.\nHere the function main uses the do notation to execute a single IO action, but do can also execute\na sequence of actions. The putStrLn function prints a string to the console. The printed string is\nconstructed by concatenating three parts: “1 + 2 = “, the result of the expression 1 + 2 (which is 3),\nand the string representation of this result, which is obtained by calling the function show.\nIt’s worth noting that putStrLn writes a string to the standard output and also writes a new line\ncharacter to the console. In general, the function show is used to convert any value to a string, here\nit is converting the result of 1+2 to string to concatenate it with the previous string.\nPure Haskell code performs no I/O, network access, access to shared in-memory data structures, etc.\nThe first time you build an example program with stack it may take a while since library\ndependencies need to be loaded from the web. In each example directory, after an initial stack\nbuild or stack ghci (to run the repl) then you should not notice this delay.\nInteractive GHCi Shell\nThe interactive shell (often called a “repl”) is very useful for learning Haskell: understanding types\nand the value of expressions. While simple expressions can be typed directly into the GHCi shell, it\nis usually better to use an external text editor and load Haskell source files into the shell (repl). Let’s\nget started. Assuming that you have installed stack as described in Appendix A, please try:\n\nTutorial on Pure Haskell Programming\n10\n1\n~/$ cd haskell_tutorial_cookbook_examples/Pure\n2\n~/haskell_tutorial_cookbook_examples/Pure$ stack ghci\n3\nUsing main module: Package `Pure' component exe:Simple with main-is file: /home/mark\\\n4\nw/BITBUCKET/haskell_tutorial_cookbook_examples/Pure/Simple.hs\n5\nConfiguring GHCi with the following packages: Pure\n6\nGHCi, version 7.10.3: http://www.haskell.org/ghc/\n:? for help\n7\n[1 of 1] Compiling Main\n( /home/markw/BITBUCKET/haskell_tutorial_cookboo\\\n8\nk_examples/Pure/Simple.hs, interpreted )\n9\nOk, modules loaded: Main.\n10\n*Main> 1 + 2\n11\n3\n12\n*Main> (1 + 2)\n13\n3\n14\n*Main> :t (1 + 2)\n15\n(1 + 2) :: Num a => a\n16\n*Main> :l Simple.hs\n17\n[1 of 1] Compiling Main\n( Simple.hs, interpreted )\n18\nOk, modules loaded: Main.\n19\n*Main> main\n20\n1 + 2 = 3\n21\n*Main>\nIf you are working in a repl and edit a file you just loaded with :l, you can then reload the last file\nloaded using :r without specifying the file name. This makes it quick and easy to edit a Haskell file\nwith an external editor like Emacs or Vi and reload it in the repl after saving changes to the current\nfile.\nHere we have evaluated a simple expression “1 + 2” in line 10. Notice that in line 12 we can always\nplace parenthesis around an expression without changing its value. We will use parenthesis when\nwe need to change the default orders of precedence of functions and operators and make the code\nmore readable.\nIn line 14 we are using the ghci :t command to show the type of the expression (1 + 2). The type\nNum is a type class (i.e., a more general purpose type that other types can inherit from) that contains\nseveral sub-types of numbers. As examples, two subtypes of Num are Fractional (e.g., 3.5) and\nInteger (e.g., 123). Type classes provide a form of function overloading since existing functions can\nbe redefined to handle arguments that are instances of new classes.\nIn line 16 we are using the ghci command :l to load the external file Simple.hs. This file contains a\nfunction called main so we can execute main after loading the file. The contents of Simple.hs is:\n\nTutorial on Pure Haskell Programming\n11\n1\nmodule Main where\n2\n3\nsum2 x y = x + y\n4\n5\nmain = do\n6\nputStrLn (\"1 + 2 = \" ++ show (sum2 1 2))\nLine 1 defines a module named Main. The rest of this file is the definition of the module. This form\nof the module do expression exports all symbols so other code loading this module has access to\nsum2 and main. If we only wanted to export main then we could use:\nmodule Main (main) where\nThe function sum2 takes two arguments and adds them together. I didn’t define the type of this\nfunction so Haskell does it for us using type inference.\n1\n*Main> :l Simple.hs\n2\n[1 of 1] Compiling Main\n( Simple.hs, interpreted )\n3\nOk, modules loaded: Main.\n4\n*Main> :t sum2\n5\nsum2 :: Num a => a -> a -> a\n6\n*Main> sum2 1 2\n7\n3\n8\n*Main> sum2 1.0 2\n9\n3.0\n10\n*Main> :t 3.0\n11\n3.0 :: Fractional a => a\n12\n*Main> :t 3\n13\n3 :: Num a => a\n14\n*Main> (toInteger 3)\n15\n3\n16\n*Main> :t (toInteger 3)\n17\n(toInteger 3) :: Integer\n18\n*Main>\nWhat if you want to build a standalone executable program from the example in Smple.hs? Here is\nan example:\n\nTutorial on Pure Haskell Programming\n12\n1\n$ stack ghc Simple.hs\n2\n[1 of 1] Compiling Main\n( Simple.hs, Simple.o )\n3\nLinking Simple ...\n4\n$ ./Simple\n5\n1 + 2 = 3\nMost of the time we will use simple types built into Haskell: characters, strings, lists, and tuples.\nThe type Char is a single character. One type of string is a list of characters [Char]. (Another type\nByteString will be covered in later chapters.) Every element in a list must have the same type. A\nTuple is like a list but elements can be different types. Here is a quick introduction to these types,\nwith many more examples later:\n1\n*Main> :t 's'\n2\n's' :: Char\n3\n*Main> :t \"tree\"\n4\n\"tree\" :: [Char]\n5\n*Main> 's' : \"tree\"\n6\n\"stree\"\n7\n*Main> :t \"tick\"\n8\n\"tick\" :: [Char]\n9\n*Main> 's' : \"tick\"\n10\n\"stick\"\n11\n*Main> :t [1,2,3,4]\n12\n[1,2,3,4] :: Num t => [t]\n13\n*Main> :t [1,2,3.3,4]\n14\n[1,2,3.3,4] :: Fractional t => [t]\n15\n*Main> :t [\"the\", \"cat\", \"slept\"]\n16\n[\"the\", \"cat\", \"slept\"] :: [[Char]]\n17\n*Main> [\"the\", \"cat\", \"slept\"] !! 0\n18\n\"the\"\n19\n*Main> head [\"the\", \"cat\", \"slept\"]\n20\n\"the\"\n21\n*Main> tail [\"the\", \"cat\", \"slept\"]\n22\n[\"cat\",\"slept\"]\n23\n*Main> [\"the\", \"cat\", \"slept\"] !! 1\n24\n\"cat\"\n25\n*Main> :t (20, 'c')\n26\n(20, 'c') :: Num t => (t, Char)\n27\n*Main> :t (30, \"dog\")\n28\n(30, \"dog\") :: Num t => (t, [Char])\n29\n*Main> :t (1, \"10 Jackson Street\", 80211, 77.5)\n30\n(1, \"10 Jackson Street\", 80211, 77.5)\n31\n:: (Fractional t2, Num t, Num t1) => (t, [Char], t1, t2)\n\nTutorial on Pure Haskell Programming\n13\nThe GHCi repl command :t tells us the type of any expression or function. Much of your time\ndeveloping Haskell will be spent with an open repl and you will find yourself checking types many\ntimes during a development session.\nIn line 1 you see that the type of ’s‘ is ’s’ :: Char and in line 3 that the type of the string “tree”\nis [Char] which is a list of characters. The abbreviation String is defined for [Char]; you can use\neither. In line 9 we see the “cons” operator : used to prepend a character to a list of characters. The\ncons : operator works with all types contained in any lists. All elements in a list must be of the same\ntype.\nThe type of the list of numbers [1,2,3,4] in line 11 is [1,2,3,4] :: Num t ⇒ [t]. The type Num is a\ngeneral number type. The expression Num t ⇒ [t] is read as: “t is a type variable equal to Num and\nthe type of the list is [t], or a list of Num values”. It bears repeating: all elements in a list must be of\nthe same type. The functions head and tail used in lines 19 and 21 return the first element of a list\nand return a list without the first element.\nYou will use lists frequently but the restriction of all list elements being the same type can be too\nrestrictive so Haskell also provides a type of sequence called tuple whose elements can be of different\ntypes as in the examples in lines 25-31.\nTuples of length 2 are special because functions fst and snd are provided to access the first and\nsecond pair value:\n*Main> fst (1, \"10 Jackson Street\")\n1\n*Main> snd (1, \"10 Jackson Street\")\n\"10 Jackson Street\"\n*Main> :info fst\nfst :: (a, b) -> a\n-- Defined in ‘Data.Tuple’\n*Main> :info snd\nsnd :: (a, b) -> b\n-- Defined in ‘Data.Tuple’\nPlease note that fst and snd will not work with tuples that are not of length 2. Also note that if you\nuse the function length on a tuple, the result is always one because of the way tuples are defined as\nFoldable types, which we will use later.\nHaskell provides a concise notation to get values out of long tuples. This notation is called\ndestructuring:\n\nTutorial on Pure Haskell Programming\n14\n1\n*Main> let geoData = (1, \"10 Jackson Street\", 80211, 77.5)\n2\n*Main> let (_,_,zipCode,temperature) = geoData\n3\n*Main> zipCode\n4\n80211\n5\n*Main> temperature\n6\n77.5\nHere, we defined a tuple geoData with values: index, street address, zip code, and temperature. In\nline two we extract the zip code and temperature. Another reminder: we use let in lines 1-2 because\nwe are in a repl.\nLike all programming languages, Haskell has operator precedence rules as these examples show:\n1\n*Main> 1 + 2 * 10\n2\n21\n3\n*Main> 1 + (2 * 10)\n4\n21\n5\n*Main> length \"the\"\n6\n3\n7\n*Main> length \"the\" + 10\n8\n13\n9\n*Main> (length \"the\") + 10\n10\n13\nThe examples in lines 1-4 illustrate that the multiplication operator has a higher precedence than\nthe addition operator.\n*Main> :t length\nlength :: Foldable t => t a -> Int\n*Main> :t (+)\n(+) :: Num a => a -> a -> a\nNote that the function length starts with a lower case letter. All Haskell functions start with a lower\ncase letter except for type constructor functions that we will get to later. A Foldable type can be\niterated through and be processed with map functions (which we will use shortly).\nWe saw that the function + acts as an infix operator. We can convert infix functions to prefix\nfunctions by enclosing them in parenthesis:\n\nTutorial on Pure Haskell Programming\n15\n*Main> (+) 1 2\n3\n*Main> div 10 3\n3\n*Main> 10 `div` 3\n3\nIn this last example we also saw how a prefix function div can be used infix by enclosing it in back\ntick characters.\n1\n*Main> let x3 = [1,2,3]\n2\n*Main> x3\n3\n[1,2,3]\n4\n*Main> let x4 = 0 : x3\n5\n*Main> x4\n6\n[0,1,2,3]\n7\n*Main> x3 ++ x4\n8\n[1,2,3,0,1,2,3]\n9\n*Main> x4\n10\n[0,1,2,3]\n11\n*Main> x4 !! 0\n12\n0\n13\n*Main> x4 !! 100\n14\n*** Exception: Prelude.!!: index too large\n15\n*Main> let myfunc1 x y = x ++ y\n16\n*Main> :t myfunc1\n17\nmyfunc1 :: [a] -> [a] -> [a]\n18\n*Main> myfunc1 x3 x4\n19\n[1,2,3,0,1,2,3]\nUsually we define functions in files and load them as we need them. Here is the contents of the file\nmyfunc1.hs:\n1\nmyfunc1 :: [a] -> [a] -> [a]\n2\nmyfunc1 x y = x ++ y\nThe first line is a type signature for the function and is not required; here the input arguments are\ntwo lists and the output is the two lists concatenated together. In line 1 note that a is a type variable\nthat can represent any type. However, all elements in the two function input lists and the output list\nare constrained to be the same type.\n\nTutorial on Pure Haskell Programming\n16\n1\n*Main> :l myfunc1.hs\n2\n[1 of 1] Compiling Main\n( myfunc1.hs, interpreted )\n3\nOk, modules loaded: Main.\n4\n*Main> myfunc1 [\"the\", \"cat\"] [\"ran\", \"up\", \"a\", \"tree\"]\n5\n[\"the\",\"cat\",\"ran\",\"up\",\"a\",\"tree\"]\nPlease note that the stack repl auto-completes using the tab character. For example, when I was\ntyping in “:l myfunc1.hs” I actually just typed “:l myf” and then hit the tab character to complete\nthe file name. Experiment with auto-completion, it will save you a lot of typing. In the following\nexample, for instance, after defining the variable sentence I can just type “se” and the tab character\nto auto-complete the entire variable name:\n1\n*Main> let sentence = myfunc1 [\"the\", \"cat\"] [\"ran\", \"up\", \"a\", \"tree\"]\n2\n*Main> sentence\n3\n[\"the\",\"cat\",\"ran\",\"up\",\"a\",\"tree\"]\nThe function head returns the first element in a list and the function tail returns all but the first\nelements in a list:\n1\n*Main> head sentence\n2\n\"the\"\n3\n*Main> tail sentence\n4\n[\"cat\",\"ran\",\"up\",\"a\",\"tree\"]\nWe can create new functions from existing arguments by supplying few arguments, a process known\nas “currying”:\n1\n*Main> let p1 = (+ 1)\n2\n*Main> :t p1\n3\np1 :: Num a => a -> a\n4\n*Main> p1 20\n5\n21\nIn this last example the function + takes two arguments but if we only supply one argument a\nfunction is returned as the value: in this case a function that adds 1 to an input value.\nWe can also create new functions by composing existing functions using the infix function . that\nwhen placed between two function names produces a new function that combines the two functions.\nLet’s look at an example that uses . to combine the partial function (+ 1) with the function length:\n\nTutorial on Pure Haskell Programming\n17\n1\n*Main> let lengthp1 = (+ 1) . length\n2\n*Main> :t lengthp1\n3\nlengthp1 :: Foldable t => t a -> Int\n4\n*Main> lengthp1 \"dog\"\n5\n4\nNote the order of the arguments to the inline function .: the argument on the right side is the first\nfunction that is applied, then the function on the left side of the . is applied.\nThis is the second example where we have seen the type Foldable which means that a type can be\nmapped over, or iterated over. We will look at Haskell types in the next section.\nIntroduction to Haskell Types\nThis is a good time to spend more time studying Haskell types. We will see more material on Haskell\ntypes throughout this book so this is just an introduction using the data expression to define a Type\nMyColors defined in the file MyColors.hs:\n1\ndata MyColors = Orange | Red | Blue | Green | Silver\n2\nderiving (Show)\nThis code defines a new data type in Haskell named MyColors that has five values: Orange, Red,\nBlue, Green or Silver. The keyword data is used to define a new data type, and the “|” symbol is\nused to separate the different possible values (also known as constructors) of the type.\nThe deriving (Show) clause at the end of the line tells the compiler to automatically generate an\nimplementation of the Show type class for the MyColors type. In other words, we are asking the\nHaskell compiler to automatically generate a function show that can convert a value to a string.\nshow is a standard function and in general we want it defined for all types. show converts an\ninstance to a string value. This allows instances of MyColors to be converted to strings using the\nfunction show.\nThe MyColors type defined here is an enumeration (i.e., it is a fixed set of values), it’s an algebraic\ndata type with no associated fields. This means that the type MyColors can only take one of the five\nvalues defined: Orange, Red, Blue, Green or Silver. There is another way to think about this. This\ncode defines a new data type called MyColors with five constructors Orange, Red, Blue, Green or\nSilver.\n\nTutorial on Pure Haskell Programming\n18\n1\nPrelude> :l colors.hs\n2\n[1 of 1] Compiling Main\n( colors.hs, interpreted )\n3\nOk, modules loaded: Main.\n4\n*Main> show Red\n5\n\"Red\"\n6\n*Main> let c1 = Green\n7\n*Main> c1\n8\nGreen\n9\n*Main> :t c1\n10\nc1 :: MyColors\n11\n*Main> Red == Green\n12\n13\n<interactive>:60:5:\n14\nNo instance for (Eq MyColors) arising from a use of ‘==’\n15\nIn the expression: Red == Green\n16\nIn an equation for ‘it’: it = Red == Green\nWhat went wrong here? The infix function == checks for equality and we did not define equality\nfunctions for our new type. Let’s fix the definition in the file colors.hs:\n1\ndata MyColors = Orange | Red | Blue | Green | Silver\n2\nderiving (Show, Eq)\nBecause we are deriving Eq we are also asking the compiler to generate code to see if two instances\nof this class are equal. If we wanted to be able to order our colors then we would also derive Ord.\nNow our new type has show, ==, and /= (inequality) defined:\n1\nPrelude> :l colors.hs\n2\n[1 of 1] Compiling Main\n( colors.hs, interpreted )\n3\nOk, modules loaded: Main.\n4\n*Main> Red == Green\n5\nFalse\n6\n*Main> Red /= Green\n7\nTrue\nLet’s also now derive Ord to have the compile generate a default function compare that operates\non the type MyColors:\n1\ndata MyColors = Orange | Red | Blue | Green | Silver\n2\nderiving (Show, Eq, Ord)\nBecause we are now deriving Ord the compiler will generate functions to calculate relative ordering\nfor values of type MyColors. Let’s experiment with this:\n\nTutorial on Pure Haskell Programming\n19\n1\n*Main> :l MyColors.hs\n2\n[1 of 1] Compiling Main\n( MyColors.hs, interpreted )\n3\nOk, modules loaded: Main.\n4\n*Main> :t compare\n5\ncompare :: Ord a => a -> a -> Ordering\n6\n*Main> compare Green Blue\n7\nGT\n8\n*Main> compare Blue Green\n9\nLT\n10\n*Main> Orange < Red\n11\nTrue\n12\n*Main> Red < Orange\n13\nFalse\n14\n*Main> Green < Red\n15\nFalse\n16\n*Main> Green < Silver\n17\nTrue\n18\n*Main> Green > Red\n19\nTrue\nNotice that the compiler generates a compare function for the type MyColors that orders values\nby the order that they appear in the data expression. What if you wanted to order them in string\nsort order? This is very simple: we will remove Ord from the deriving clause and define our own\nfunction compare for type MyColors instead of letting the compiler generate it for us:\n1\ndata MyColors = Orange | Red | Blue | Green | Silver\n2\nderiving (Show, Eq)\n3\n4\ninstance Ord MyColors where\n5\ncompare c1 c2 = compare (show c1) (show c2)\nIn line 5 I am using the function show to convert instances of MyColors to strings and then the\nversion of compare that is called in line 5 is the version the compiler wrote for us because we derived\nShow. Now the ordering is in string ascending sort order because we are using the compare function\nthat is supplied for the type String:\n1\n*Main> :l MyColors.hs\n2\n[1 of 1] Compiling Main\n( MyColors.hs, interpreted )\n3\nOk, modules loaded: Main.\n4\n*Main> Green > Red\n5\nFalse\n\nTutorial on Pure Haskell Programming\n20\nOur new type MyColors is a simple type. Haskell also supports hierarchies of types called Type\nClasses and the type we have seen earlier Foldable is an example of a type class that other types\ncan inherit from. For now, consider sub-types of Foldable to be collections like lists and trees that\ncan be iterated over.\nI want you to get in the habit of using :type and :info (usually abbreviated to :t and :i) in the GHCi\nrepl. Stop reading for a minute now and type :info Ord in an open repl. You will get a lot of output\nshowing you all of the types that Ord is defined for. Here is a small bit of what gets printed:\n1\n*Main> :i Ord\n2\nclass Eq a => Ord a where\n3\ncompare :: a -> a -> Ordering\n4\n(<) :: a -> a -> Bool\n5\n(<=) :: a -> a -> Bool\n6\n(>) :: a -> a -> Bool\n7\n(>=) :: a -> a -> Bool\n8\nmax :: a -> a -> a\n9\nmin :: a -> a -> a\n10\n-- Defined in ‘ghc-prim-0.4.0.0:GHC.Classes’\n11\ninstance Ord MyColors -- Defined at MyColors.hs:4:10\n12\ninstance (Ord a, Ord b) => Ord (Either a b)\n13\n-- Defined in ‘Data.Either’\n14\ninstance Ord a => Ord [a]\n15\n-- Defined in ‘ghc-prim-0.4.0.0:GHC.Classes’\n16\ninstance Ord Word -- Defined in ‘ghc-prim-0.4.0.0:GHC.Classes’\n17\ninstance Ord Ordering -- Defined in ‘ghc-prim-0.4.0.0:GHC.Classes’\n18\ninstance Ord Int -- Defined in ‘ghc-prim-0.4.0.0:GHC.Classes’\n19\ninstance Ord Float -- Defined in ‘ghc-prim-0.4.0.0:GHC.Classes’\n20\ninstance Ord Double -- Defined in ‘ghc-prim-0.4.0.0:GHC.Classes’\nLines 1 through 8 show you that Ord is a subtype of Eq that defines functions compare, max, and\nmin as well as the four operators <, <=, >=, and >=. When we customized the compare function for\nthe type MyColors, we only implemented compare. That is all that we needed to do since the other\noperators rely on the implementation of compare.\nOnce again, I ask you to experiment with the example type MyColors in an open GHCi repl:\n\nTutorial on Pure Haskell Programming\n21\n1\n*Main> :t max\n2\nmax :: Ord a => a -> a -> a\n3\n*Main> :t Green\n4\nGreen :: MyColors\n5\n*Main> :i Green\n6\ndata MyColors = ... | Green | ...\n-- Defined at MyColors.hs:1:39\n7\n*Main> max Green Red\n8\nRed\nThe following diagram shows a partial type hierarchy of a few types included in the standard Haskell\nPrelude (this is derived from the Haskell Report at haskell.org¹³):\nExample Haskell Type Hierarchy\nHere you see that type Num and Ord are sub-types of type Eq, Real is a sub-type of Num, etc. We\nwill see the types Monad and Functor in the next chapter.\nFunctions Are Pure\nAgain, it is worth pointing out that Haskell functions do not modify their inputs values. The common\npattern is to pass immutable values to a function and modified values are returned. As a first example\nof this pattern we will look at the standard function map that takes two arguments: a function that\n¹³https://www.haskell.org/onlinereport/basic.html\n\nTutorial on Pure Haskell Programming\n22\nconverts a value of any type a to another type b, and a list of type a. Functions that take other\nfunctions as arguments are called higher order functions. The result is another list of the same\nlength whose elements are of type b and the elements are calulated using the function passed as the\nfirst argument. Let’s look at a simple example using the function (+ 1) that adds 1 to a value:\n1\n*Main> :t map\n2\nmap :: (a -> b) -> [a] -> [b]\n3\n*Main> map (+ 1) [10,20,30]\n4\n[11,21,31]\n5\n*Main> map (show . (+ 1)) [10,20,30]\n6\n[\"11\",\"21\",\"31\"]\nIn the first example, types a and b are the same, a Num. The second example used a composed\nfunction that adds 1 and then converts the example to a string. Remember: the function show\nconverts a Haskell data value to a string. In this second example types a and b are different because\nthe function is mapping a number to a string.\nThe directory haskell_tutorial_cookbook_examples/Pure contains the examples for this chapter. We\npreviously used the example file Simple.hs. Please note that in the rest of this book I will omit the\ngit repository top level directory name haskell_tutorial_cookbook_examples and just specify the\nsub-directory name:\n1\nmodule Main where\n2\n3\nsum2 x y = x + y\n4\n5\nmain = do\n6\nputStrLn (\"1 + 2 = \" ++ show (sum2 1 2))\nFor now let’s just look at the mechanics of executing this file without using the REPL (started with\nstack ghci). We can simply build and run this example using stack, which is covered in some detail\nin Appendix A:\nstack build --exec Simple\nThis command builds the project defined in the configuration files Pure.cabal and stack.yaml\n(the format and use of these files is briefly covered in detail in Appendix A and there is more\nreference material here¹⁴). This example defines two functions: sum2 and main. sum2 is a pure\nHaskell function with no state, no interaction with the outside world like file IO, etc., and no non-\ndeterminism. main is an impure function, and we will look at impure Haskell code in some detail\nin the next chapter. As you might guess the output of this code snippet is\n¹⁴https://docs.haskellstack.org/en/stable/yaml_configuration/\n\nTutorial on Pure Haskell Programming\n23\n1 + 2 = 3\nTo continue the tutorial on using pure Haskell functions, once again we will use stack to start an\ninteractive repl during development:\n1\nmarkw@linux:~/haskell_tutorial_cookbook_examples/Pure$ stack ghci\n2\n*Main> :t 3\n3\n3 :: Num a => a\n4\n*Main> :t \"dog\"\n5\n\"dog\" :: [Char]\n6\n*Main> :t main\n7\nmain :: IO ()\n8\n*Main>\nIn this last listing I don’t show the information about your Haskell environment and the packages\nthat were loaded. In repl listings in the remainder of this book I will continue to edit out this Haskell\nenvironment information for brevity.\nLine 4 shows the use of the repl shortcut :t to print out the type of a string which is an array of\n[Char], and the type of the function main is of type IO Action, which we will explain in the next\nchapter. An IO action contains impure code where we can read and write files, perform a network\noperation, etc. and we will look at IO Action in the next chapter.\nUsing Parenthesis or the Special $ Character and\nOperator Precedence\nWe will look at operator and function precedence and the use of the $ character to simplify using\nparenthesis in expessions. By the way, in Haskell there is not much difference between operators\nand function calls except operators like +, etc. which are by default infix while functions are usually\nprefix. So except for infix functions that are enclosed in backticks (e.g., 10 div 3) Haskell usually\nuses prefix functions: a function followed by zero or more arguments. You can also use $ that acts\nas an opening parenthesis with a not-shown closing parenthesis at the end of the current expression\n(which may be multi-line). Here are some examples:\n\nTutorial on Pure Haskell Programming\n24\n1\n*Main> print (3 * 2)\n2\n6\n3\n*Main> print $ 3 * 2\n4\n6\n5\n*Main> last (take 10 [1..])\n6\n10\n7\n*Main> last $ take 10 [1..]\n8\n10\n9\n*Main> ((take 10 [1..]) ++ (take 10 [1000..]))\n10\n[1,2,3,4,5,6,7,8,9,10,1000,1001,1002,1003,1004,1005,1006,1007,1008,1009]\n11\n*Main> take 10 [1..] ++ take 10 [1000..]\n12\n[1,2,3,4,5,6,7,8,9,10,1000,1001,1002,1003,1004,1005,1006,1007,1008,1009]\n13\n*Main> 1 + 2 * (4 * 5)\n14\n41\n15\n*Main> 2 * 3 + 10 * 30\n16\n306\nI use the GHCi command :info (:i is an abbreviation) to check both operator precedence and the\nfunction signature if the operator is converted to a function by enclosing it in parenthessis:\n1\n*Main> :info *\n2\nclass Num a where\n3\n...\n4\n(*) :: a -> a -> a\n5\n...\n6\n-- Defined in ‘GHC.Num’\n7\ninfixl 7 *\n8\n*Main> :info +\n9\nclass Num a where\n10\n(+) :: a -> a -> a\n11\n...\n12\n-- Defined in ‘GHC.Num’\n13\ninfixl 6 +\n14\n*Main> :info `div`\n15\nclass (Real a, Enum a) => Integral a where\n16\n...\n17\ndiv :: a -> a -> a\n18\n...\n19\n-- Defined in ‘GHC.Real’\n20\ninfixl 7 `div`\n21\n*Main> :i +\n22\nclass Num a where\n23\n(+) :: a -> a -> a\n\nTutorial on Pure Haskell Programming\n25\n24\n...\n25\n-- Defined in ‘GHC.Num’\n26\ninfixl 6 +\nNotice how + has lower precedence than *.\nJust to be clear, understand how operators are used as functions and also how functions can be used\nas infix operators:\n1\n*Main> 2 * 3\n2\n6\n3\n*Main> (*) 2 3\n4\n6\n5\n*Main> 10 `div` 3\n6\n3\n7\n*Main> div 10 3\n8\n3\nEspecially when you are just starting to use Haskell it is a good idea to also use :info to check the\ntype signatures of standard functions that you use. For example:\n1\n*Main> :info last\n2\nlast :: [a] -> a\n-- Defined in ‘GHC.List’\n3\n*Main> :info map\n4\nmap :: (a -> b) -> [a] -> [b]\n-- Defined in ‘GHC.Base’\nLazy Evaluation\nHaskell is refered to as a lazy language because expressions are not evaluated until they are used.\nConsider the following example:\n1\n$ stack ghci\n2\n*Main> [0..10]\n3\n[0,1,2,3,4,5,6,7,8,9,10]\n4\n*Main> take 11 [0..]\n5\n[0,1,2,3,4,5,6,7,8,9,10]\n6\n*Main> let xs = [0..]\n7\n*Main> :sprint xs\n8\nxs = _\n9\n*Main> take 5 xs\n10\n[0,1,2,3,4]\n\nTutorial on Pure Haskell Programming\n26\n11\n*Main> :sprint xs\n12\nxs = _\n13\n*Main>\nIn line 2 we are creating a list with 11 elements. In line 4 we are doing two things:\n• Creating an infinitely long list containing ascending integers starting with 0.\n• Fetching the first 11 elements of this infinitely long list. It is important to understand that in\nline 4 only the first 11 elements are generated because that is all the take function requires.\nIn line 6 we are assigning another infinitely long list to the variable xs but the value of xs is\nunevaluated and a placeholder is stored to calculate values as required. In line 7 we use GHCi’s\n:sprint command to show a value without evaluating it. The output in line 8 _ indicated that the\nexpression has yet to be evaluated.\nLines 9 through 12 remind us that Haskell is a functional language: the take function used in line 9\ndoes not change the value of its argument so xs as seen in lines 10 and 12 is still unevaluated.\nUnderstanding List Comprehensions\nEffectively using list comprehensions makes your code shorter, easier to understand, and easier to\nmaintain. Let’s start out with a few GHCi repl examples. You will learn a new GHCi repl trick in\nthis section: entering multiple line expressions by using :{ and :} to delay evaluation until an entire\nexpression is entered in the repl (listings in this section are reformatted to fit the page width):\n1\n*Main> [x | x <- [\"cat\", \"dog\", \"bird\"]]\n2\n[\"cat\",\"dog\",\"bird\"]\n3\n*Main> :{\n4\n*Main| [(x,y) | x <- [\"cat\", \"dog\", \"bird\"],\n5\n*Main|\ny <- [1..2]]\n6\n*Main| :}\n7\n[(\"cat\",1),(\"cat\",2),(\"dog\",1),(\"dog\",2),(\"bird\",1),(\"bird\",2)]\nThe list comprehension on line 1 assigns the elements of the list [“cat”, “dog”, “bird”] one at a time\nto the variable x and then collects all these values of x in a list value that is the value of the list\ncomprehension. The list comprehension in line 1 is hopefully easy to understand but when we bind\nand collect multiple variables the situation, as seen in the example in lines 4 and 5, is not as easy to\nunderstand. The thing to remember is that the first variable gets iterated as an “outer loop” and the\nsecond variable is iterated as the “inner loop.” List comprehensions can use many variables and the\niteration ordering rule is the same: last variable iterates first, etc.\n\nTutorial on Pure Haskell Programming\n27\n*Main> :{\n*Main| [(x,y) | x <- [0..3],\n*Main|\ny <- [1,3..10]]\n*Main| :}\n[(0,1),(0,3),(0,5),(0,7),(0,9),(1,1),(1,3),(1,5),(1,7),\n(1,9),(2,1),(2,3),(2,5),(2,7),(2,9),(3,1),(3,3),(3,5),\n(3,7),(3,9)]\n*Main> [1,3..10]\n[1,3,5,7,9]\nIn this last example we are generating all combinations of [0..3] and [1,3..10] and storing the\ncombinations as two element tuples. You could also store then as lists:\n1\n*Main> [[x,y] | x <- [1,2], y <- [10,11]]\n2\n[[1,10],[1,11],[2,10],[2,11]]\nList comprehensions can also contain filtering operations. Here is an example with one filter:\n1\n*Main> :{\n2\n*Main| [(x,y) | x <- [\"cat\", \"dog\", \"bird\"],\n3\n*Main|\ny <- [1..10],\n4\n*Main|\ny `mod` 3 == 0]\n5\n*Main| :}\n6\n[(\"cat\",3),(\"cat\",6),(\"cat\",9),\n7\n(\"dog\",3),(\"dog\",6),(\"dog\",9),\n8\n(\"bird\",3),(\"bird\",6),(\"bird\",9)]\nHere is a similar example with two filters (we are also filtering out all possible values of x that start\nwith the character ‘d’):\n1\n*Main> :{\n2\n*Main| [(x,y) | x <- [\"cat\", \"dog\", \"bird\"],\n3\n*Main|\ny <- [1..10],\n4\n*Main|\ny `mod` 3 == 0,\n5\n*Main|\nx !! 0 /= 'd']\n6\n*Main| :}\n7\n[(\"cat\",3),(\"cat\",6),(\"cat\",9),(\"bird\",3),(\"bird\",6),(\"bird\",9)]\nFor simple filtering cases I usually use the filter function but list comprehensions are more versatile.\nList comprehensions are extremely useful - I use them frequently.\nLists are instances of the class Monad that we will cover in the next chapter (check out the section\n“List Comprehensions Using the do Notation”).\nList comprehensions are powerful. I would like to end this section with another trick that does not\nuse list comprehensions for building lists of tuple values: using the zip function:\n\nTutorial on Pure Haskell Programming\n28\n1\n*Main> let animals = [\"cat\", \"dog\", \"bird\"]\n2\n*Main> zip [1..] animals\n3\n[(1,\"cat\"),(2,\"dog\"),(3,\"bird\")]\n4\n*Main> :info zip\n5\nzip :: [a] -> [b] -> [(a, b)]\n-- Defined in ‘GHC.List’\nThe function zip is often used in this way when we have a list of objects and we want to operate on\nthe list while knowing the index of each element.\nHaskell Rules for Indenting Code\nWhen a line of code is indented relative to the previous line of code, or several lines of code with\nadditional indentation, then the indented lines act as if they were on the previous line. In other\nwords, if code that should all be on one line must be split to multiple lines, then use indentation as\na signal to the Haskell compiler.\nIndentation of continuation lines should be uniform, starting in the same column. Here are some\nexamples of good code, and code that will not compile:\n1\nlet a = 1\n-- good\n2\nb = 2\n-- good\n3\nc = 3\n-- good\n4\n5\nlet\n6\na = 1\n-- good\n7\nb = 2\n-- good\n8\nc = 3\n-- good\n9\nin a + b + c\n-- good\n10\n11\nlet a = 1\n-- will not compile (bad)\n12\nb = 2\n-- will not compile (bad)\n13\nc = 3\n-- will not compile (bad)\n14\n15\nlet\n16\na = 1\n-- will not compile (bad)\n17\nb = 2\n-- will not compile (bad)\n18\nc = 3\n-- will not compile (bad)\n19\n20\nlet {\n21\na = 1;\n-- compiles but bad style (good)\n22\nb = 2;\n-- compiles but bad style (good)\n23\nc = 3;\n-- compiles but bad style (good)\n24\n}\n\nTutorial on Pure Haskell Programming\n29\nIf you use C style braces and semicolons to mark end of expressions, then indenting does not matter\nas seen in lines 20 through 24. Otherwise, uniform indentation is a hint to the compiler.\nThe same indenting rules apply to other types of do expressions which we will see throughout this\nbook for do, if, and other types of do expressions.\nUnderstanding let and where\nAt first glance, let and where seem very similar in that they allow us to create temporary variables\nused inside functions. As the examples in the file LetAndWhere.hs show, there are important\ndifferences.\nIn the following code notice that when we use let in pure code inside a function, we then use in to\nindicate the start of an expression to be evaluated that uses any variables defined in a let expression.\nInside a do code block the in token is not needed and will cause a parse error if you use it. do code\nblocks are a syntactic sugar for use in impure Haskell code and we will use it frequently later in the\nbook.\nYou also do not use in inside a list comprehension as seen in the function testLetComprehension\nin the next code listing:\n1\nmodule Main where\n2\n3\nfunnySummation w x y z =\n4\nlet bob = w + x\n5\nsally = y + z\n6\nin bob + sally\n7\n8\ntestLetComprehension =\n9\n[(a,b) | a <- [0..5], let b = 10 * a]\n10\n11\ntestWhereBlocks a =\n12\nz * q\n13\nwhere\n14\nz = a + 2\n15\nq = 2\n16\n17\nfunctionWithWhere n\n=\n18\n(n + 1) * tenn\n19\nwhere\n20\ntenn = 10 * n\n21\n22\nmain = do\n\nTutorial on Pure Haskell Programming\n30\n23\nprint $ funnySummation 1 2 3 4\n24\nlet n = \"Rigby\"\n25\nprint n\n26\nprint testLetComprehension\n27\nprint $ testWhereBlocks 11\n28\nprint $ functionWithWhere 1\nCompare the let do expressions starting on line 4 and 24. The first let occurs in pure code and uses\nin to define one or more do expressions using values bound in the let. In line 24 we are inside a\nmonad, specifically using the do notation and here let is used to define pure values that can be used\nlater in the do do expression.\nLoading the last code example and running the main function produces the following output:\n1\n*Main> :l LetAndWhere.hs\n2\n[1 of 1] Compiling Main\n( LetAndWhere.hs, interpreted )\n3\nOk, modules loaded: Main.\n4\n*Main> main\n5\n10\n6\n\"Rigby\"\n7\n[(0,0),(1,10),(2,20),(3,30),(4,40),(5,50)]\n8\n26\n9\n20\nThis output is self explanatory except for line 7 that is the result of calling testLetComprehension\nthat retuns an example list comprehension [(a,b)|a<-[0..5],letb=10*a]\nConditional do Expressions and Anonymous Functions\nThe examples in the next three sub-sections can be found in haskell_tutorial_cookbook_exam-\nples/Pure/Conditionals.hs. You should read the following sub-sections with this file loaded (some\nGHCi repl output removed for brevity):\n1\nhaskell_tutorial_cookbook_examples/Pure$ stack ghci\n2\n*Main> :l Conditionals.hs\n3\n[1 of 1] Compiling Main\n( Conditionals.hs, interpreted )\n4\nOk, modules loaded: Main.\n5\n*Main>\n\nTutorial on Pure Haskell Programming\n31\nSimple Pattern Matching\nWe previously used the built-in functions head that returns the first element of a list and tail that\nreturns a list with the first element removed. We will define these functions ourselves using what\nis called wild card pattern matching. It is common to append the single quote character ‘ to built-in\nfunctions when we redefine them so we name our new functions head’ and tail’. Remember when\nwe used destructuring to access elements of a tuple? Wild card pattern matching is similar:\nhead'(x:_)\n= x\ntail'(_:xs) = xs\nThe underscore character _ matches anything and ignores the matched value. Our head and tail\ndefinitions work as expected:\n1\n*Main> head' [\"bird\",\"dog\",\"cat\"]\n2\n\"bird\"\n3\n*Main> tail' [0,1,2,3,4,5]\n4\n[1,2,3,4,5]\n5\n*Main> :type head'\n6\nhead' :: [t] -> t\n7\n*Main> :t tail'\n8\ntail' :: [t] -> [t]\nOf course we frequently do not want to ignore matched values. Here is a contrived example that\nexpects a list of numbers and doubles the value of each element. As for all of the examples in this\nchapter, the following function is pure: it can not modify its argument(s) and always returns the\nsame value given the same input argument(s):\n1\ndoubleList [] = []\n2\ndoubleList (x:xs) = (* 2) x : doubleList xs\nIn line 1 we start by defining a pattern to match the empty list. It is necessary to define this\nterminating condition because we are using recursion in line 2 and eventually we reach the end\nof the input list and make the recursive call doubleList []. If you leave out line 1 you then will see\na runtime error like “Non-exhaustive patterns in function doubleList.” As a Haskell beginner you\nprobably hate Haskell error messages and as you start to write your own functions in source files\nand load them into a GHCi repl or compile them, you will initially probably hate compilation error\nmessages also. I ask you to take on faith a bit of advice: Haskell error messages and warnings will\nend up saving you a lot of effort getting your code to work properly. Try to develop the attitude\n“Great! The Haskell compiler is helping me!” when you see runtime errors and compiler errors.\nIn line 2 notice how I didn’t need to use extra parenthesis because of the operator and function\napplication precedence rules.\n\nTutorial on Pure Haskell Programming\n32\n1\n*Main> doubleList [0..5]\n2\n[0,2,4,6,8,10]\n3\n*Main> :t doubleList\n4\ndoubleList :: Num t => [t] -> [t]\nThis function doubleList seems very unsatisfactory because it is so specific. What if we wanted to\ntriple or quadruple the elements of a list? Do we want to write two new functions? You might think\nof adding an argument that is the multiplier like this:\n1\nbumpList n [] = []\n2\nbumpList n (x:xs) = n * x : bumpList n xs\nis better, being more abstract and more general purpose. However, we will do much better.\nBefore generalizing the list manipuation process further, I would like to make a comment on coding\nstyle, specifically on not using unneeded parenthesis. In the last exmple defining bumpList if you\nhave superfluous parenthesis like this:\nbumpList n (x:xs) = (n * x) : bumpList (n xs)\nthen the code still works correctly and is fairly readable. I would like you to get in the habit\nof avoiding extra uneeded parenthesis and one tool for doing this is running hlint (installing\nhlint is covered in Appendix A) on your Haskell code. Using hlint source file will provide\nwarnings/suggestions like this:\nhaskell_tutorial_cookbook_examples/Pure$ hlint Conditionals.hs\nConditionals.hs:7:21: Warning: Redundant bracket\nFound:\n((* 2) x) : doubleList (xs)\nWhy not:\n(* 2) x : doubleList (xs)\nConditionals.hs:7:43: Error: Redundant bracket\nFound:\n(xs)\nWhy not:\nxs\nhlint is not only a tool for improving your code but also for teaching you how to better program using\nHaskell. Please note that hlint provides other suggestions for Conditionals.hs that I am ignoring that\nmostly suggest that I replace our mapping operations with using the built-in map function and use\nfunctional composition. The sample code is specifically to show examples of pattern matching and\nis not as concise as it could be.\n\nTutorial on Pure Haskell Programming\n33\nAre you satisfied with the generality of the function bumpList? I hope that you are not! We should\nwrite a function that will apply an arbitrary function to each element of a list. We will call this\nfunction map’ to avoid confusing our map’ function with the built-in function map.\nThe following is a simple implementation of a map function (we will see Haskell’s standard map\nfunctions in the next section):\n1\nmap' f [] = []\n2\nmap' f (x:xs) = f x : map' f xs\nIn line 2 we do not need parenthesis around f x because function application has a higher precidence\nthan the operator : which adds an element to the beginning of a list.\nAre you pleased with how concise this definition of a map function is? Is concise code like map’\nreadable to you? Speaking as someone who has written hundreds of thousands of lines of Java code\nfor customers, let me tell you that I love the conciseness and readability of Haskell! I appreciate\nthe Java ecosystem with many useful libraries and frameworks and augmented like fine languages\nlike Clojure and JRuby, but in my opinion using Haskell is a more enjoyable and generally more\nproductive language and programming environment.\nLet’s experiment with our map’ function:\n1\n*Main> map' (* 7) [0..5]\n2\n[0,7,14,21,28,35]\n3\n*Main> map' (+ 1.1) [0..5]\n4\n[1.1,2.1,3.1,4.1,5.1,6.1]\n5\n*Main>\nmap' (\\x -> (x + 1) * 2) [0..5]\n6\n[2,4,6,8,10,12]\nLines 1 and 3 should be understandable to you: we are creating a partial function like (* 7) and\npassing it to map’ to apply to the list [0..5].\nThe syntax for the function in line 5 is called an anonymous function. Lisp programers, like myself,\nrefer to this as a lambda expression. In any case, I often prefer using anonymous functions when a\nfunction will not be used elsewhere. In line 5 the argement to the anonymous inline function is x\nand the body of the function is (x + 1) * 2.\nI do ask you to not get carried away with using too many anonymous inline functions because they\ncan make code a little less readable. When we put our code in modules, by default every symbol\n(like function names) in the module is externally visible. However, if we explicitly export symbols\nin a module do expression then only the explicitly exported symbols are visible by other code that\nuses the module. Here is an example:\n\nTutorial on Pure Haskell Programming\n34\nmodule Test2 (doubler) where\nmap' f [] = []\nmap' f (x:xs) = (f x) : map' f xs\ntestFunc x = (x + 1) * 2\ndoubler xs = map' (* 2) xs\nIn this example map’ and testFunc are hidden: any other module that imports Test2 only has access\nto doubler. It might help for you to think of the exported functions roughly as an interface for a\nmodule.\nPattern Matching With Guards\nWe will cover two important concepts in this section: using guard pattern matching to make function\ndefinitions shorter and easier to read and we will look at the Maybe type and how it is used. The\nMaybe type is mostly used in non-pure Haskell code and we will use it heavily later. The Maybe\ntype is a Monad (covered in the next chapter). I introduce the Maybe type here since its use fits\nnaturally with guard patterns.\nGuards are more flexible than the pattern matching seen in the last section. I use pattern matching\nfor simple cases of destructuring data and guards when I need the flexibility. You may want to revisit\nthe examples in the last section after experimenting with and understanding the examples seen here.\nThe examples for this section are in the file Guards.hs. As a first simple example we will implement\nthe Ruby language “spaceship operator”:\n1\nspaceship n\n2\n| n < 0\n= -1\n3\n| n == 0\n= 0\n4\n| otherwise = 1\nNotice on line 1 that we do not use an = in the function definition when using guards. Each guard\nstarts with |, contains a condition, and a value on the right side of the = sign.\n1\n*Main> spaceship (-10)\n2\n-1\n3\n*Main> spaceship 0\n4\n0\n5\n*Main> spaceship 17\n6\n1\nRemember that a literal negative number as seen in line 1 must be wrapped in parenthesis, otherwise\nthe Haskell compiler will interpret - as an operator.\n\nTutorial on Pure Haskell Programming\n35\nCase Expressions\nCase do expressions match a value against a list of possible values. It is common to use the wildcard\nmatching value _ at the end of a case expression which can be of any type. Here is an example in\nthe file Cases.hs:\n1\nmodule Main where\n2\n3\nnumberOpinion n =\n4\ncase n of\n5\n0 -> \"Too low\"\n6\n1 -> \"just right\"\n7\n_ -> \"OK, that is a number\"\n8\n9\nmain = do\n10\nprint $ numberOpinion 0\n11\nprint $ numberOpinion 1\n12\nprint $ numberOpinion 2\nThe code in lines 3-7 defines the function numberOpinion that takes a single argument “n”. We\nuse a case expression to match the value of n against several possible cases. Each of these cases is\ndefined using the -> operator, followed by an expression to be evaluated if the case is matched.\nThe first case, 0 -> ‘Too low’ matches the value of n against 0, if the value of “n” is 0, the function\nwill return the string “Too low”. The second case, 1 -> ‘just right’ matches the value of n against 1,\nif the value of n is 1, the function will return the string “just right”. The last case is different in that\nit is a catch all case using the ** as a wild card match. So, ** -> ‘OK, that is a number’ matches\nany other values of n: if the value of nn is not 0 or 1 the function will return the string “OK, that is\na number”.\nIf Then Else expressions\nHaskell has if then else syntax built into the language - if is not defined as a function. Personally I\ndo not use if then else in Haskell very often. I mostly use simple pattern matching and guards. Here\nare some short examples from the file IfThenElses.hs:\nageToString age =\nif age < 21 then \"minor\" else \"adult\"\nAll if statements must have both a then expression and a else expression.\n\nTutorial on Pure Haskell Programming\n36\nhaskell_tutorial_cookbook_examples/Pure$ stack ghci\n*Main> :l IfThenElses.hs\n[1 of 1] Compiling Main\n( IfThenElses.hs, interpreted )\nOk, modules loaded: Main.\n*Main> ageToString 15\n\"minor\"\n*Main> ageToString 37\n\"adult\"\nMaps\nMaps are simple to construct using a list of key-value tuples and are by default immutable. There is\nan example using mutable maps in the next chapter.\nWe will look at the module Data.Map first in a GHCi repl, then later in a few full code examples.\nThere is something new in line 1 of the following listing: I am assigning a short alias M to the module\nData.Map. In referencing a function like fromList (which converts a list of tuples to a map) in the\nData.Map module I can use M.fromList instead of Data.Map.fromList. This is a common practice\nso when you read someone else’s Haskell code, one of the first things you should do when reading\na Haskell source file is to make note of the module name abbreviations at the top of the file.\n1\nhaskell_tutorial_cookbook_examples/Pure$ stack ghci\n2\n*Main> import qualified Data.Map as M\n3\n*Main M> :t M.fromList\n4\nM.fromList :: Ord k => [(k, a)] -> M.Map k a\n5\n*Main M> let aTestMap = M.fromList [(\"height\", 120), (\"weight\", 15)]\n6\n*Main M> :t aTestMap\n7\naTestMap :: Num a => M.Map [Char] a\n8\n*Main M> :t lookup\n9\nlookup :: Eq a => a -> [(a, b)] -> Maybe b\n10\n*Main M> :t M.lookup\n11\nM.lookup :: Ord k => k -> M.Map k a -> Maybe a\n12\n*Main M> M.lookup \"weight\" aTestMap\n13\nJust 15\n14\n*Main M> M.lookup \"address\" aTestMap\n15\nNothing\nThe keys in a map must all be the same type and the values are also constrained to be of the same\ntype. I almost always create maps using the helper function fromList in the module Data.Maps.\nWe will only be using this method of map creation in later examples in this book so I am skipping\ncoverage of other map building functions. I refer you to the Data.Map documentation¹⁵.\n¹⁵https://www.stackage.org/haddock/lts-6.17/containers-0.5.6.2/Data-Map.html\n\nTutorial on Pure Haskell Programming\n37\nThe following example shows one way to use the Just and Nothing return values:\n1\nmodule MapExamples where\n2\n3\nimport qualified Data.Map as M -- from library containers\n4\n5\naTestMap = M.fromList [(\"height\", 120), (\"weight\", 15)]\n6\n7\ngetNumericValue key aMap =\n8\ncase M.lookup key aMap of\n9\nNothing -> -1\n10\nJust value -> value\n11\n12\nmain = do\n13\nprint $ getNumericValue \"height\" aTestMap\n14\nprint $ getNumericValue \"age\" aTestMap\nThe function getNumericValue shows one way to extract a value from an instance of type Maybe.\nThe function lookup returns a Maybe value and in this example I use a case statement to test for\na Nothing value or extract a wrapped value in a Just instance. Using Maybe in Haskell is a better\nalternative to checking for null values in C or Java.\nThe output from running the main function in module MapExamples is:\n1\nhaskell_tutorial_cookbook_examples/Pure$ stack ghci\n2\n*Main> :l MapExamples.hs\n3\n[1 of 1] Compiling MapExamples\n( MapExamples.hs, interpreted )\n4\nOk, modules loaded: MapExamples.\n5\n*MapExamples> main\n6\n120\n7\n-1\nSets\nThe documentation of Data.Set.Class can be found here¹⁶ and contains overloaded functions for\nthe types of sets defined here¹⁷.\nFor most of my work and for the examples later in this book, I create immutable sets from lists and\nthe only operation I perform is checking to see if a value is in the set. The following examples in\nGHCI repl are what you need for the material in this book:\n¹⁶https://www.stackage.org/haddock/lts-6.17/sets-0.0.5/Data-Set-Class.html\n¹⁷https://www.stackage.org/package/sets\n\nTutorial on Pure Haskell Programming\n38\n1\n*Main> import qualified Data.Set as S\n2\n*Main S> let testSet = S.fromList [\"cat\",\"dog\",\"bird\"]\n3\n*Main S> :t testSet\n4\ntestSet :: S.Set [Char]\n5\n*Main S> S.member \"bird\" testSet\n6\nTrue\n7\n*Main S> S.member \"snake\" testSet\n8\nFalse\nSets and Maps are immutable so I find creating maps using a lists of key-value tuples and creating\nsets using lists is fine. That said, coming from the mutable Java, Ruby, Python, and Lisp programming\nlanguages, it took me a while to get used to immutability in Haskell.\nMore on Functions\nIn this section we will review what you have learned so far about Haskell functions and then look\nat a few more complex examples.\nWe have been defining and using simple functions and we have seen that operators behave like infix\nfunctions. We can make operators act as prefix functions by wrapping them in parenthesis:\n*Main> 10 + 1\n11\n*Main> (+) 10 1\n11\nand we can make functions act as infix operators:\n*Main> div 100 9\n11\n*Main> 100 `div` 9\n11\nThis back tick function to operator syntax works with functions we write also:\n\nTutorial on Pure Haskell Programming\n39\n*Main> let myAdd a b = a + b\n*Main> :t myAdd\nmyAdd :: Num a => a -> a -> a\n*Main> myAdd 1 2\n3\n*Main> 1 `myAdd` 2\n3\nBecause we are working in a GHCi repl, in line 1 we use let to define the function myAdd. If you\ndefined this function in a file and then loaded it, you would not use a let.\nIn the map examples where we applied a function to a list of values, so far we have used functions\nthat map input values to the same return type, like this (using both partial function evaluation and\nanonymous inline function):\n*Main> map (* 2) [5,6]\n[10,12]\n*Main> map (\\x -> 2 * x) [5,6]\n[10,12]\nWe can also map to different types; in this example we map from a list of Num values to a list\ncontaining sub-lists of Num values:\n1\n*Main> let makeList n = [0..n]\n2\n*Main> makeList 3\n3\n[0,1,2,3]\n4\n*Main> map makeList [2,3,4]\n5\n[[0,1,2],[0,1,2,3],[0,1,2,3,4]]\nAs usual, I recommend that when you work in a GHCi repl you check the types of functions and\nvalues you are working with:\n1\n*Main> :t makeList\n2\nmakeList :: (Enum t, Num t) => t -> [t]\n3\n*Main> :t [1,2]\n4\n[1,2] :: Num t => [t]\n5\n*Main> :t [[0,1,2],[0,1,2,3],[0,1,2,3,4]]\n6\n[[0,1,2],[0,1,2,3],[0,1,2,3,4]] :: Num t => [[t]]\n7\n*Main>\nIn line 2 we see that for any type t the function signature is t -> [t] where the compiler determines\nthat t is constrained to be a Num or Enum by examining how the input variable is used as a range\nparameter for constructing a list. Let’s make a new function that works on any type:\n\nTutorial on Pure Haskell Programming\n40\n1\n*Main> let make3 x = [x,x,x]\n2\n*Main> :t make3\n3\nmake3 :: t -> [t]\n4\n*Main> :t make3 \"abc\"\n5\nmake3 \"abc\" :: [[Char]]\n6\n*Main> make3 \"abc\"\n7\n[\"abc\",\"abc\",\"abc\"]\n8\n*Main> make3 7.1\n9\n[7.1,7.1,7.1]\n10\n*Main> :t make3 7.1\n11\nmake3 7.1 :: Fractional t => [t]\nNotice in line 3 that the function make3 takes any type of input and returns a list of elements the\nsame type as the input. We used makes3 both with a string argument and a fractional (floating\npoint) number) argument.\nComments on Dealing With Immutable Data and How\nto Structure Programs\nIf you program in other programming languages that use mutable data then expect some feelings of\ndisorientation initially when starting to use Haskell. It is common in other languages to maintain the\nstate of a computation in an object and to mutate the value(s) in that object. While I cover mutable\nstate in the next chapter the common pattern in Haskell is to create a data structure (we will use lists\nin examples here) and pass it to functions that return a new modified copy of the data structure as\nthe returned value from the function. It is very common to keep passing the modified new copy of a\ndata structure through a series of function calls. This may seem cumbersome when you are starting\nto use Haskell but quickly feels natural.\nThe following example shows a simple case where a list is constructed in the function main and\npassed through two functions doubleOddElements and times10Elements:\n1\nmodule ChainedCalls where\n2\n3\ndoubleOddElements =\n4\nmap (\\x -> if x `mod` 2 == 0 then x else 2 * x)\n5\n6\ntimes10Elements = map (* 10)\n7\n8\nmain = do\n9\nprint $ doubleOddElements [0,1,2,3,4,5,6,7,8]\n10\nlet aList = [0,1,2,3,4,5]\n11\nlet newList = times10Elements $ doubleOddElements aList\n\nTutorial on Pure Haskell Programming\n41\n12\nprint newList\n13\nlet newList2 = (times10Elements . doubleOddElements) aList\n14\nprint newList2\nNotice that the expressions being evaluated in lines 11 and 13 are the same. In line 11 we are\napplying function doubleOddElements to the value of aList and passing this value to the outer\nfunction times10Elements. In line 13 we are creating a new function from composing two existing\nfunctions: times10Elements . doubleOddElements. The parenthesis in line 13 are required because\nthe . operator has lower precedence than the application of function doubleOddElements so without\nthe parenthesis line 13 would evaluate as times10Elements (doubleOddElements aList) which is\nnot what I intended and would throw an error.\nThe output is:\n1\nhaskell_tutorial_cookbook_examples/Pure$ stack ghci\n2\n*Main> :l ChainedCalls.hs\n3\n[1 of 1] Compiling ChainedCalls\n( ChainedCalls.hs, interpreted )\n4\nOk, modules loaded: ChainedCalls.\n5\n*ChainedCalls> main\n6\n[0,2,2,6,4,10,6,14,8]\n7\n[0,20,20,60,40,100]\n8\n[0,20,20,60,40,100]\nUsing immutable data takes some getting used to. I am going to digress for a minute to talk about\nworking with Haskell. The steps I take when writing new Haskell code are:\n• Be sure I understand the problem\n• How will data be represented - in Haskell I prefer using built-in types when possible\n• Determine which Haskell standard functions, modules, and 3rd party modules might be useful\n• Write and test the pure Haskell functions I think that I need for the application\n• Write an impure main function that fetches required data, calls the pure functions (which are\nno longer pure in the sense they are called from impure code), and saves the processed data.\nI am showing you many tiny examples but please keep in mind the entire process of writing longer\nprograms.\nError Handling\nWe have seen examples of handling soft errors when no value can be calculated: use Maybe, Just,\nand Nothing. In bug free pure Haskell code, runtime exceptions should be very rare and I usually\ndo not try to trap them.\nUsing Maybe, Just, and Nothing is much better than, for example, throwing an error using the\nstandard function error:\n\nTutorial on Pure Haskell Programming\n42\n*Main> error \"test error 123\"\n*** Exception: test error 123\nand then, in impure code catching the errors, here is the documentation¹⁸ for your reference.\nIn impure code that performs IO or accesses network resources that could possibly run out of\nmemory, etc., runtime errors can occur and you could use the same try catch coding style that you\nhave probably used in other programming languages. I admit this is my personal coding style but I\ndon’t like to catch runtime errors. I spent a long time writing Java applications and when possible I\npreferred using uncaught exceptions and I usually do the same when writing impure Haskell code.\nBecause of Haskell’s type safety and excellent testing tools, it is possible to write nearly error free\nHaskell code. Later when we perform network IO we will rely on library support to handle errors\nand timeouts in a clean “Haskell like” way.\nTesting Haskell Code\nThe example in this section is found in the directory haskell_tutorial_cookbook_examples/Testing-\nHaskell.\nIf you use stack to create a new project then the framework for testing is generated for you:\n$ stack new TestingHaskell\n$ cd TestingHaskell\n$\nls -R\nLICENSE\napp\ntest\nSetup.hs\nsrc\nTestingHaskell.cabal\nstack.yaml\nTestingHaskell//app:\nMain.hs\nTestingHaskell//src:\nLib.hs\nTestingHaskell//test:\nSpec.hs\n$ cat test/Spec.hs\nmain :: IO ()\nmain = putStrLn \"Test suite not yet implemented\"\n$ stack setup\n$ stack build\n¹⁸https://wiki.haskell.org/Exception\n\nTutorial on Pure Haskell Programming\n43\nThis stack generated project is more complex than the project I created manually in the directory\nhaskell_tutorial_cookbook_examples/Pure. The file Setup.hs is a placeholder and uses any module\nnamed Main in the app directory. This module, defined in app/Main.hs, imports the module Lib\ndefined in src/Lib.hs.\nThe generated test does not do anything, but let’s run it anyway:\n$ stack test\nRegistering TestingHaskell-0.1.0.0...\nTestingHaskell-0.1.0.0: test (suite: TestingHaskell-test)\nProgress: 1/2 Test suite not yet implemented\nCompleted 2 action(s).\nIn the generated project, I made a few changes:\n• removed src/Lib.hs\n• added src/MyColors.hs providing the type MyColors that we defined earlier\n• modified app/Main.hs to use the MyColors type\n• added tests to test/Spec.hs\nHere is the contents of TestingHaskell/src/MyColors.hs:\nmodule MyColors where\ndata MyColors = Orange | Red | Blue | Green | Silver\nderiving (Show, Eq)\ninstance Ord MyColors where\ncompare c1 c2 = compare (show c1) (show c2)\nAnd the new test/Spec.hs file:\n\nTutorial on Pure Haskell Programming\n44\n1\nimport Test.Hspec\n2\n3\nimport MyColors\n4\n5\nmain :: IO ()\n6\nmain = hspec spec\n7\n8\nspec :: Spec\n9\nspec = do\n10\ndescribe \"head\" $ do\n11\nit \"test removing first list element\" $ do\n12\nhead [1,2,3,4] `shouldBe` 1\n13\nhead [\"the\", \"dog\", \"ran\"] `shouldBe` \"dog\" -- should fail\n14\ndescribe \"MyColors tests\" $ do\n15\nit \"test custom 'compare' function\" $ do\n16\nMyColors.Green < MyColors.Red `shouldBe` True\n17\nRed > Silver `shouldBe` True\n-- should fail\nNotice how two of the tests are meant to fail as an example. Let’s run the tests:\n1\n$ stack test\n2\nTestingHaskell-0.1.0.0: test (suite: TestingHaskell-test)\n3\n4\nProgress: 1/2\n5\nhead\n6\ntest removing first list element FAILED [1]\n7\nMyColors tests\n8\ntest custom 'compare' function FAILED [2]\n9\n10\nFailures:\n11\n12\ntest/Spec.hs:13:\n13\n1) head test removing first list element\n14\nexpected: \"dog\"\n15\nbut got: \"the\"\n16\n17\ntest/Spec.hs:17:\n18\n2) MyColors tests test custom 'compare' function\n19\nexpected: True\n20\nbut got: False\n21\n22\nRandomized with seed 1233887367\n23\n\nTutorial on Pure Haskell Programming\n45\n24\nFinished in 0.0139 seconds\n25\n2 examples, 2 failures\n26\n27\nCompleted 2 action(s).\n28\nTest suite failure for package TestingHaskell-0.1.0.0\n29\nTestingHaskell-test:\nexited with: ExitFailure 1\n30\nLogs printed to console\nIn line one with stack test we are asking stack to run app tests in the subdirectory test. All Haskell\nsource files in subdirectory test are assumed to be test files. In the listing for file test/Spec.hs we have\ntwo tests that fail on purpose and you see the output for the failed tests at lines 12-15 and 17-20.\nBecause the Haskell compiler does such a good job at finding type errors I have fewer errors in my\nHaskell code compared to languages like Ruby and Common Lisp. As a result I find myself writing\nfewer tests for my Haskell code than I would write in other languages. Still, I recommend some tests\nfor each of your projects; decide for yourself how much relative effort you want to put into writing\ntests.\nPure Haskell Wrap Up\nI hope you are starting to get an appreciation for using composition of functions and higher order\nfunctions to enable us to compose programs from smaller pieces that can be joined together.\nThis composition is made easier when using pure functions that always return the same value when\ncalled with the same type of arguments.\nWe will continue to see examples of how lazy evaluation simplifies code because we can use infinitely\nlarge lists with the assurance that values are not calculated until they are needed.\nIn addition to Haskell code generally having fewer errors (after it gets by the compiler!) other\nadvantages of functional programming include more concise code that is easy to read and understand\nonce you get some experience with the language.\n\nTutorial on Impure Haskell\nProgramming\nOne of the great things about Haskell is that the language encourages us to think of our code in two\nparts:\n• Pure functional code (functions have no side effects) that is easy to write and test. Functional\ncode tends to be shorter and less likely to be imperative (i.e., more functional, using maps and\nrecursion, and less use of loops as in Java or C++).\n• Impure code that deals with side effects like file and network IO, maintaining state in a typesafe\nway, and isolate imperative code that has side effects.\nIn his excellent Functional Programming with Haskell class at eDX¹⁹ Erik Meijer described pure code\nas being islands in the ocean and the ocean representing impure code. He says that it is a design\ndecision how much of your code is pure (islands) and how much is impure (the ocean). This model\nof looking at Haskel programs works for me.\nMy use the word “impure” is common for refering to Haskell code with side effects. Haskell is a\npurely functional language and side effects like I/O are best handled in a pure functional way using\nby wrapping pure values in Mondads.\nIn addition to showing you reusable examples of impure code that you will likely need in your own\nprograms, a major theme of this chapter is handling impure code in a convenient type safe fashion.\nAny Monad, which wraps a single value, is used to safely manage state. I will introduce you to\nusing Monad types as required for the examples in this chapter. This tutorial style introduction will\nprepare you for understanding the sample applications later.\nHello IO () Monad\nI showed you many examples of pure code in the last chapter but most examples in source files (as\nopposed to those shown in a GHCi repl) had a bit of impure code in them: the main function like\nthe following that simply writes a string of characters to standard output:\nmain = do\nprint \"hello world\"\nThe type of function main is:\n¹⁹http://edx.org\n\nTutorial on Impure Haskell Programming\n47\n*Main> :t main\nmain :: IO ()\nThe IO () monad is an IO value wrapped in a type safe way. Because Haskell is a lazy evaluation\nlanguage, the value is not evaluated until it is used. Every IO () action returns exactly one value.\nThink of the word “mono” (or “one”) when you think of Monads because they always return one\nvalue. Monads are also used to connnect together parts of a program.\nWhat is it about the function main in the last example that makes its type an IO ()? Consider the\nsimple main function here:\nmodule NoIO where\nmain = do\nlet i = 1 in\n2 * i\nand its type:\n*Main> :l NoIO\n[1 of 1] Compiling NoIO\n( NoIO.hs, interpreted )\nOk, modules loaded: NoIO.\n*NoIO> main\n2\n*NoIO> :t main\nmain :: Integer\n*NoIO>\nOK, now you see that there is nothing special about a main function: it gets its type from the type\nof value returned from the function. It is common to have the return type depend on the function\nargument types. The first example returns a type IO () because it returns a print do expression:\n*Main> :t print\nprint :: Show a => a -> IO ()\n*Main> :t putStrLn\nputStrLn :: String -> IO ()\nThe function print shows the enclosing quote characters when displaying a string while putStrLn\ndoes not. In the first example, what happens when we stitch together several expressions that have\ntype IO ()? Consider:\n\nTutorial on Impure Haskell Programming\n48\nmain = do\nprint 1\nprint \"cat\"\nFunction main is still of type IO (). You have seen do expressions frequently in examples and now\nwe will dig into what the do expression is and why we use it.\nThe do notation makes working with monads easier. There are alternatives to using do that we will\nlook at later.\nOne thing to note is that if you are doing bindings inside a do expression using a let with a in\nexpression, you need to wrap the bindings in a new (inner) do expression if there is more than one\nline of code following the let statement. The way to avoid requiring a nested do expression is to not\nuse in in a let expression inside a do block of code. Yes, this sounds complicated but let’s clear up\nany confusion by looking at the examples found in the file ImPure/DoLetExample.hs (you might also\nwant to look at the similar example file ImPure/DoLetExample2.hs that uses bind operators instead\nof a do statement; we will look at bind operators in the next section):\nmodule DoLetExample where\nexample1 = do\n-- good style\nputStrLn \"Enter an integer number:\"\ns <- getLine\nlet number = (read s :: Int) + 2\nputStrLn $ \"Number plus 2 = \" ++ (show number)\nexample2 = do\n-- avoid using \"in\" inside a do statement\nputStrLn \"Enter an integer number:\"\ns <- getLine\nlet number = (read s :: Int) + 2 in\nputStrLn $ \"Number plus 2 = \" ++ (show number)\nexample3 = do\n-- avoid using \"in\" inside a do statement\nputStrLn \"Enter an integer number:\"\ns <- getLine\nlet number = (read s :: Int) + 2 in\ndo -- this do is required since we have two dependent statements:\nputStrLn \"Result is:\"\nputStrLn $ \"Number plus 2 = \" ++ (show number)\nmain = do\nexample1\nexample2\nexample3\n\nTutorial on Impure Haskell Programming\n49\nYou should use the pattern in function example1 and not the pattern in example2. The do expression\nis syntactic sugar that allows programmers to string together a sequence of operations that can mix\npure and impure code.\nTo be clear, the left arrow <- is used when the expression on the right side is some type of IO () that\nneeds to be lifted before being used. A let do expression is used when the right side expression is a\npure value.\nOn lines 6 and 12 we are using function read to converting a string read out of IO String () to an\ninteger value. Remember that the value of s (from calling readLine) is an IO () so in the same way\nyou might read from a file, in this example we are reading a value from an IO () value.\nA Note About >> and >>= Operators\nSo far in this book I have been using the syntactic sugar of the do expression to work with Monads\nlike IO () and I will usually use this syntactic sugar for the rest of this book.\nEven though I find it easier to write and read code using do, many Haskell programmers prefer >>\nand >>= so let’s go over these operators so you won’t be confused when reading Haskell code that\nuses them. Also, when we use do expressions in code the compiler generates similar code using these\n>> and >>= operators.\nThe Monad type class defines the operators >>= and return. We turn to the GHCi repl to experiment\nwith and learn about these operators:\n1\n*Main> :t (>>)\n2\n(>>) :: Monad m => m a -> m b -> m b\n3\n*Main> :t (>>=)\n4\n(>>=) :: Monad m => m a -> (a -> m b) -> m b\n5\n*Main> :t return\n6\nreturn :: Monad m => a -> m a\nWe start with the return function type return :: Monad m ⇒ a -> m a which tells us that for a\nmonad m the function return takes a value and wraps it in a monad. We will see examples of the\nreturn function used to return a wrapped value from a function that returns IO () values. The bind\noperator (>>) is used to evaluate two expressions in sequence. As an example, we can replace this\ndo expression:\nmain = do\nexample1\nexample2\nexample3\nwith the following:\n\nTutorial on Impure Haskell Programming\n50\nmain = example1 >> example2 >> example3\nThe operator >>= is similar to >> except that it evaluates the left hand expression and pipes its value\ninto the right hand side expression. The left hand side expression is evaluated to some type of IO\n() and the expression on the right hand side typically reads from the input IO (). An example will\nmake this simpler to understand:\n1\nmodule DoLetExample3 where\n2\n3\nexample3 =\nputStrLn \"Enter an integer number:\" >>\ngetLine\n4\n5\nexample4 mv = do\n6\nlet number = (read mv :: Int) + 2\n7\nputStrLn $ \"Number plus 2 = \" ++ (show number)\n8\n9\nmain = example3 >>= example4\nNote that I could have used a do statement to define function example3 but used a bind operator\ninstead. Let’s run this example and look at the function types. Please don’t just quickly read through\nthe following listing; when you understand what is happening in this example then for the rest of\nyour life programming in Haskell things will be easier for you:\n1\n*DoLetExample3> main\n2\nEnter an integer number:\n3\n1\n4\nNumber plus 2 = 3\n5\n*DoLetExample3> :t example3\n6\nexample3 :: IO String\n7\n*DoLetExample3> :t example4\n8\nexample4 :: String -> IO ()\n9\n*DoLetExample3> :t main\n10\nmain :: IO ()\n11\n*DoLetExample3> let x = example3\n12\n*DoLetExample3> x\n13\nEnter an integer number:\n14\n4\n15\n\"4\"\n16\n*DoLetExample3> :t x\n17\nx :: IO String\n18\n*DoLetExample3> x >>= example4\n19\nEnter an integer number:\n20\n3\n21\nNumber plus 2 = 5\n\nTutorial on Impure Haskell Programming\n51\nThe interesting part starts at line 11 when we define x to be the returned value from calling example3.\nRemember that Haskell is a lazy language: evaluation is postponed until a value is actually used.\nWorking inside a GHCi repl is like working interactively inside a do expression. When we evaluate\nx in line 12 then the code in function example3 is actually executed (notice this is where the user\nprompt to enter a number occurs). In line 18 we are re-evaluationg the value in x and passing the\nresulting IO String () value to the function example4.\nHaskell is a “piecemeal” programming language as are the Lisp family of languages where a repl\nis used to write little pieces code that are collected into programs. For simple code in Haskell (and\nLisp languages) I do sometimes directly enter code into a text editor but very ofter I start in a repl,\nexperiment, debug, refine, and then copy into an edited file.\nConsole IO Example with Stack Configuration\nThe directory CommandLineApps contains two simple applications that interact with STDIO, that\nis to write to the console and read from the keyboard. The first example can be found in file\nCommandLineApp/CommandLine1.hs:\n1\nmodule Main where\n2\n3\nimport System.IO\n4\nimport Data.Char (toUpper)\n5\n6\nmain = do\n7\nputStrLn \"Enter a line of text for test 1:\"\n8\ns <- getLine\n9\nputStrLn $ \"As upper case:\\t\" ++ (map toUpper s)\n10\nmain\nLines 3 and 4 import the entire System.IO module (that is, import all exported symbols from\nSystem.IO) and just the function toUpper from module Data.Char. System.IO is a standard Haskell\nmodule and we do not have to do anything special to import it. The Data.Char is stored in the\npackage text. The package text is contained in the library package base which is specified in the\nCommandLineApp.cabal configuration file that we will look at soon.\nUse of the <- assignment in line 8 in the last Haskell listing is important to understand. It might\noccur to you to leave out line 8 and just place the getLine function call directly in line 9, like this:\n1\nputStrLn $ \"As upper case:\\t\" ++ (map toUpper getLine)\nIf you try this (please do!) you will see compilation errors like:\n\nTutorial on Impure Haskell Programming\n52\n1\nCouldn't match expected type ‘[Char]’ with actual type ‘IO String’\n2\nIn the second argument of ‘map’, namely ‘getLine’\n3\nIn the second argument of ‘(++)’, namely ‘(map toUpper getLine)’\nThe type of getLine is an IO () that is a wrapped IO call. The value is not computed until it is used.\nThe <- assignment in line 8 evaluates the IO call and unwraps the result of the IO operation so that\nit can be used.\nI don’t spend much time covering stack project configuration files in this book but I do recommend\nthat as you work through examples to also look for a file in each example directory ending with the\nfile extension .cabal that specified which packages need to be loaded. For some examples it might\ntake a while to download and configure libraries the first time you run either stack build or stack\nghci in an example directory.\nThe Haskell stack project in the CommandLineApp directory has five target applications as we\ncan see in the CommandLineApp.cabal file. I am not going to go into much detail about the project\ncabal and stack.yaml files generated by stack when you create a new project except for configuration\ndata that I had to add manually; in this case, I added two executable targets at the end of the cabal\nfile (note: the project in the github repository for this book has more executable targets, I just show\na few here):\n1\nexecutable CommandLine1\n2\nhs-source-dirs:\n.\n3\nmain-is:\nCommandLine1.hs\n4\ndefault-language:\nHaskell2010\n5\nbuild-depends:\nbase >= 4.7 && < 5\n6\n7\nexecutable CommandLine2\n8\nhs-source-dirs:\n.\n9\nmain-is:\nCommandLine2.hs\n10\ndefault-language:\nHaskell2010\n11\nbuild-depends:\nbase >= 4.7 && < 5\n12\n13\nexecutable ReadTextFile\n14\nhs-source-dirs:\n.\n15\nmain-is:\nReadTextFile.hs\n16\ndefault-language:\nHaskell2010\n17\nbuild-depends:\nbase >= 4.7 && < 5\n18\n19\nexecutable GameLoop1\n20\nhs-source-dirs:\n.\n21\nmain-is:\nGameLoop1.hs\n22\ndefault-language:\nHaskell2010\n23\nbuild-depends:\nbase >= 4.7 && < 5, time\n\nTutorial on Impure Haskell Programming\n53\n24\n25\nexecutable GameLoop2\n26\nhs-source-dirs:\n.\n27\nmain-is:\nGameLoop2.hs\n28\ndefault-language:\nHaskell2010\n29\nbuild-depends:\nbase >= 4.7 && < 5, random\nThe executable name determines the compiled and linked executable file name. For line 1, an\nexecutable file “CommandLine1” (or “CommandLine1.exe”” on Windows) will be generated. The\nparameter hs-source-dirs is a comma separated list of source file directories. In this simple example\nall Haskell source files are in the project’s top level directory “../”. The build-depends is a comma\nseparated list of module libraries; here we only use the base built-in modules packaged with Haskell.\nLet’s use a GHCi repl to poke at this code and understand it better. The project defined in\nCommandLineApp/CommandLineApp.cabal contains many executable targets so when we enter\na GHCi repl, the available targets are shown and you can choose one; in this case I am selecting the\nfirst target defined in the cabal file. In later GHCi repl listings, I will edit out this output for brevity:\n1\n$ stack ghci\n2\n3\n* * * * * * * *\n4\nThe main module to load is ambiguous. Candidates are:\n5\n1. Package `CommandLineApp' component exe:CommandLine1 with main-is file: /Users/mar\\\n6\nkw/GITHUB/haskell_tutorial_cookbook_examples/CommandLineApp/CommandLine1.hs\n7\n2. Package `CommandLineApp' component exe:CommandLine2 with main-is file: /Users/mar\\\n8\nkw/GITHUB/haskell_tutorial_cookbook_examples/CommandLineApp/CommandLine2.hs\n9\n3. Package `CommandLineApp' component exe:ReadTextFile with main-is file: /Users/mar\\\n10\nkw/GITHUB/haskell_tutorial_cookbook_examples/CommandLineApp/ReadTextFile.hs\n11\nYou can specify which one to pick by:\n12\n* Specifying targets to stack ghci e.g. stack ghci CommandLineApp:exe:CommandLine1\n13\n* Specifying what the main is e.g. stack ghci --main-is CommandLineApp:exe:CommandL\\\n14\nine1\n15\n* Choosing from the candidate above [1..3]\n16\n* * * * * * * *\n17\n18\nSpecify main module to use (press enter to load none): 1\n19\nLoading main module from cadidate 1, --main-is /Users/markw/GITHUB/haskell_tutorial_\\\n20\ncookbook_examples/CommandLineApp/CommandLine1.hs\n21\n22\nConfiguring GHCi with the following packages: CommandLineApp\n23\nGHCi, version 7.10.3: http://www.haskell.org/ghc/\n:? for help\n24\nOk, modules loaded: none.\n25\n[1 of 1] Compiling Main\n( /Users/markw/GITHUB/haskell_tutorial_cookbook_\\\n\nTutorial on Impure Haskell Programming\n54\n26\nexamples/CommandLineApp/CommandLine1.hs, interpreted )\n27\nOk, modules loaded: Main.\n28\n*Main> :t main\n29\nmain :: IO b\n30\n*Main> :info main\n31\nmain :: IO b\n32\n-- Defined at /Users/markw/GITHUB/haskell_tutorial_cookbook_examples/CommandLineApp/\\\n33\nCommandLine1.hs:6:1\n34\n*Main> :t getLine\n35\ngetLine :: IO String\n36\n*Main> :t putStrLn\n37\nputStrLn :: String -> IO ()\n38\n*Main> main\n39\nEnter a line of text for test 1:\n40\nline 1\n41\nAs upper case:\nLINE 1\n42\nEnter a line of text for test 1:\n43\nline 2\n44\nAs upper case:\nLINE 2\n45\nEnter a line of text for test 1:\n46\n^C Interrupted.\n47\n*Main>\nIn line 36 the function getLine is of type getLine :: IO String which means that calling getLine\nreturns a value that is a computation to get a line of text from stdio but the IO operation is not\nperformed until the value is used.\nPlease note that it is unusual to put five executable targets in a project’s cabal file. I am only doing so\nhere because I wanted to group five similar examples together in this subdirectory of the github repo\nfor this book²⁰. This repo has 16 example subdirectories, and the number would be much greater if\nI didn’t collect similar examples together.\nWe will use the example in file CommandLine2.hs in the next section which is similar to this example\nbut also appends the user input to a text file.\nFile IO\nWe will now look at a short example of doing file IO. We will write Haskell simple string values\nto a file. If you are using the more efficient Haskell Text values, the code is the same. Text values\nare more efficient than simple string values when dealing with a lot of data and we will later use\na compiler setting to automatically convert between the underlying formats. The following listing\nshows CommandLineApp/CommandLine2.hs:\n²⁰https://github.com/mark-watson/haskell_tutorial_cookbook_examples\n\nTutorial on Impure Haskell Programming\n55\n1\nmodule Main where\n2\n3\nimport System.IO\n4\nimport Data.Char (toUpper)\n5\n6\nmain = do\n7\nputStrLn \"Enter a line of text for test2:\"\n8\ns <- getLine\n9\nputStrLn $ \"As upper case:\\t\" ++ (map toUpper s)\n10\nappendFile \"temp.txt\" $ s ++ \"\\n\"\n11\nmain\nNote the use of recursion in line 11 to make this program loop forever until you use a COntrol-c to\nstop the program.\nIn line 10 we are using function appendFile to open a file, append a string to it, and then close the\nfile. appendFile is of type appendFile :: FilePath -> String -> IO (). It looks like we are passing a\nsimple string as a file name instead of type FilePath but if you look up the definition of FilePath\nyou will see that it is just an alias for string: type FilePath = String.\nRunning this example in a GHCi repl, with much of the initial printout from running stack ghci not\nshown:\n1\n$ stack ghci\n2\nCommandLineApp-0.1.0.0: configure\n3\nSpecify main module to use (press enter to load none): 2\n4\nOk, modules loaded: Main.\n5\n*Main> main\n6\nEnter a line of text for test2:\n7\nline 1\n8\nAs upper case:\nLINE 1\n9\nEnter a line of text for test2:\n10\nline 2\n11\nAs upper case:\nLINE 2\n12\nEnter a line of text for test2:\n13\n^C Interrupted.\n14\n*Main>\nThe file temp.txt was just created.\nThe next example used ReadTextFile.hs to read the file temp.txt and process the text by finding all\nwords in the file:\n\nTutorial on Impure Haskell Programming\n56\n1\nmodule Main where\n2\n3\nimport System.IO\n4\nimport Control.Monad\n5\n6\nmain = do\n7\nentireFileAsString <- readFile \"temp.txt\"\n8\nprint entireFileAsString\n9\nlet allWords = words entireFileAsString\n10\nprint allWords\nreadFile is a high-level function because it manages for you reading a file and closing the file handle\nit uses internally. The built in function words splits a string on spaces and returns a list of strings\n[String] that are printed on line 7:\n1\n$ stack ghci\n2\nCommandLineApp-0.1.0.0: build\n3\nSpecify main module to use (press enter to load none): 3\n4\nOk, modules loaded: ReadTextFile.\n5\n*ReadTextFile> main\n6\n\"line 1\\nline 2\\n\"\n7\n[\"line\",\"1\",\"line\",\"2\"]\n8\n*ReadTextFile>\n9\n*ReadTextFile> :t readFile\n10\nreadFile :: FilePath -> IO String\n11\n*ReadTextFile> :type words\n12\nwords :: String -> [String]\nWhat if the function readFile encounters an error? That is the subject for the next section.\nError Handling in Impure Code\nI know you have been patiently waiting to see how we handle errors in Haskell code. Your wait is\nover! We will look at several common types of runtime errors and how to deal with them. In the last\nsection we used the function readFile to read the contents of a text file temp.txt. What if temp.txt\ndoes not exist? Well, then we get an error like the following when running the example program in\nReadTextFile.hs:\n*Main> main\n*** Exception: temp.txt: openFile: does not exist (No such file or directory)\n\nTutorial on Impure Haskell Programming\n57\nLet’s modify this last example in a new file ReadTextFileErrorHandling.hs that catches a file not\nfound error. The following example is derived from the first example in Michael Snoyman’s article\nCatching all exceptions²¹. This example does not work inside threads; if you need to catch errors\ninside a thread then see the second example in Michael’s article.\n1\nmodule Main where\n2\n3\nimport System.IO\n4\nimport Control.Exception\n5\n6\n-- catchAny by Michael Snoyman:\n7\ncatchAny :: IO a -> (SomeException -> IO a) -> IO a\n8\ncatchAny = Control.Exception.catch\n9\n10\nsafeFileReader :: FilePath -> IO String\n11\nsafeFileReader fPath = do\n12\nentireFileAsString <- catchAny (readFile \"temp.txt\") $ \\error -> do\n13\nputStrLn $ \"Error: \" ++ show error\n14\nreturn \"\"\n15\nreturn entireFileAsString\n16\n17\nmain :: IO ()\n18\nmain = do\n19\nfContents <- safeFileReader \"temp.txt\"\n20\nprint fContents\n21\nprint $ words fContents\nI will run this twice: the first time without the file temp.txt present and a second time with temp.txt\nin the current durectory:\n*Main> :l ReadTextFileErrorHandling.hs\n[1 of 1] Compiling Main\n( ReadTextFileErrorHandling.hs, interpreted )\nOk, modules loaded: Main.\n*Main> main\nError: temp.txt: openFile: does not exist (No such file or directory)\n\"\"\n[]\n1\n*Main> main\n\"line 1\\nline 2\\n\"\n[\"line\",\"1\",\"line\",\"2\"]\n²¹https://www.schoolofhaskell.com/user/snoyberg/general-haskell/exceptions/catching-all-exceptions\n\nTutorial on Impure Haskell Programming\n58\nUntil you need to handle runtime errors in a multi-threaded Haskell program, following this example\nshould be sufficient. In the next section we look at Network IO.\nNetwork IO\nWe will experiment with three network IO examples in this book:\n• A simple socket client/server example in this section.\n• Reading web pages in the chapter “Web Scraping”\n• Querying remote RDF endpoints in the chapter “Linked Data and the Semantic Web”\nWe start by using a high level library, network-simple for both the client and serve examples in\nthe next two sub-sections. The client and sever examples are in the directory haskell_tutorial_-\ncookbook_examples/ClientServer in the files Client.hs and Server.hs.\nServer Using network-simple Library\nThe Haskell Network and Network.Simple modules use strings represented as Data.ByteString.Char8\ndata so as seen in line 1 I set the language type OverloadedStrings. The following example in file\nClientServer/Server.hs is derived from an example in the network-simple project:\n1\n{-# LANGUAGE OverloadedStrings #-}\n2\n3\nmodule Server where\n4\n5\nimport Control.Monad\n6\nimport qualified Data.ByteString.Char8 as B\n7\nimport qualified Network.Simple.TCP as T\n8\n9\nreverseStringLoop sock = do\n10\nmbs <- T.recv sock 4096\n11\ncase mbs of\n12\nJust bs -> T.send sock (B.reverse bs) >> reverseStringLoop sock\n13\nNothing -> return ()\n14\n15\nmain :: IO ()\n16\nmain = T.withSocketsDo $ do -- derived from library example\n17\nT.listen \"*\" \"3000\" $ \\(lsock, laddr) -> do\n18\nputStrLn $ \"Listening at \" ++ show laddr\n19\nforever . T.acceptFork lsock $ \\(sock, addr) -> do\n20\nputStrLn $ \"Connection from \" ++ show addr\n21\nreverseStringLoop sock\n\nTutorial on Impure Haskell Programming\n59\nThe server accepts a string, reverses the string, and returns the reversed string to the client.\nI am assuming that you have done some network programming and are familiar with sockets, etc.\nThe function reverseStringLoop defined in lines 9-13 accepts a socket as a parameter and returns\na value of type MonadIO that wraps a byte-string value. In line 10 we use the T.recv function that\ntakes two arguments: a socket and the maximum number of bytes to received from the client. The\ncase expression reverses the received byte string, sends the reversed string back to the client, and\nrecursively calls itself waiting for new data from the client. If the client breaks the socket connection,\nthen the function retuns an empty MonadIO().\nThe main function defined in lines 15-21 listens on port 3000 for new client socket connections. In\nline 19, the function T.acceptFork accepts as an argument a socket value and a function to execute;\nthe complete type is:\n1\n*Main> :t T.acceptFork\n2\nT.acceptFork\n3\n:: transformers-0.4.2.0:Control.Monad.IO.Class.MonadIO m =>\n4\nT.Socket\n5\n-> ((T.Socket, T.SockAddr) -> IO ()) -> m GHC.Conc.Sync.ThreadId\nDon’t let line 3 scare you; the GHCi repl is just showing you where this type of MonadIO is defined.\nThe return type refers to a thread ID that is passed to the function forever :: Monad m ⇒ m a -> m\nb that is defined in the module Control.Monad and lets the thread run until it teminates.\nThe network-simple package is fairly high level and relatively simple to use. If you are interested\nyou can find many client/server examples on the web that use the lower-level network package.\nWe will develop a client application to talk with this server in the next section but if you want to\nimmediately try the server, start it and then run telnet in another terminal window:\nPrelude> :l Server\n[1 of 1] Compiling Server\n( Server.hs, interpreted )\nOk, modules loaded: Server.\n*Main> main\nListening at 0.0.0.0:3000\nAnd run telnet:\n\nTutorial on Impure Haskell Programming\n60\n$ telnet localhost 3000\nTrying 127.0.0.1...\nConnected to localhost.\nEscape character is '^]'.\n12345\n54321\nThe dog ran down the street\nteerts eht nwod nar god ehT\nIn the next section we write a simple client to talk with this service example.\nClient Using network-simple Library\nI want to use automatic conversion between strings represented as Data.ByteString.Char8 data and\nregular [Char] strings so as seen in line 1 I set the language type OverloadedStrings in the example\nin file Client.hs:\n1\n{-# LANGUAGE OverloadedStrings #-}\n2\n3\nmodule Client where\n4\n5\nimport Control.Monad\n6\nimport qualified Network.Simple.TCP as T\n7\n8\nmain = do\n9\nT.connect \"127.0.0.1\" \"3000\" $ \\(connectionSocket, remoteAddr) -> do\n10\nputStrLn $ \"Connection established to \" ++ show remoteAddr\n11\nT.send connectionSocket \"test123\"\n12\nresponse <- T.recv connectionSocket 100\n13\ncase response of\n14\nJust s -> putStrLn $ \"Response: \" ++ show s\n15\nNothing -> putStrLn \"No response from server\"\nThe function T.connect in line 9 accepts arguments for a host name, a port, and a function to call\nwith the connection socket to the server and the server’s address. The body of this inline function,\ndefined in in the middle on line 9 and continuing in lines 10-15, prints the server address, sends a\nstring “test123” to the server, and waits for a response back from the server (T.recv in line 12). The\nserver response is printed, or a warning that no response was received.\nWhile the example in file Server.hs is running in another terminal, we can run the client interactively:\n\nTutorial on Impure Haskell Programming\n61\nPrelude> :l Client.hs\n[1 of 1] Compiling Client\n( Client.hs, interpreted )\nOk, modules loaded: Client.\n*Main main\nConnection established to 127.0.0.1:3000\nResponse: \"321tset\"\nA Haskell Game Loop that Maintains State\nFunctionally\nThe example in this section can be found in the file GameLoop2.hs in the directory haskell_tuto-\nrial_cookbook_examples/CommandLineApp. This example uses the random package to generate\na seed random number for a simple number guessing game. An alternative implementation in\nGameLoop1.hs, which I won’t discuss, uses the system time to generate a seed.\nThis is an important example because it demonstrates one way to maintain state in a functional\nway. We have a read-only game state value that is passed to the function gameLoop which modifies\nthe read-only game state passed as an argument and returns a newly constructed game state as the\nfunction’s returned value. This is a common pattern that we will see again later when we develop an\napplication to play a simplified version of the card game Blackjack in the chapter “Haskell Program\nto Play the Blackjack Card Game.”\n1\nmodule GameLoop2 where\n2\n3\nimport System.Random\n4\n5\ndata GameState = GameState { numberToGuess::Integer, numTries::Integer}\n6\nderiving (Show)\n7\n8\ngameLoop :: GameState -> IO GameState\n9\ngameLoop gs = do\n10\nprint $ numberToGuess gs\n11\nputStrLn \"Enter a number:\"\n12\ns <- getLine\n13\nlet num = read s :: Integer\n14\nif num == numberToGuess gs then\n15\nreturn gs\n16\nelse gameLoop $ GameState (numberToGuess gs) ((numTries gs) + 1)\n17\n18\nmain = do\n19\npTime <- randomRIO(1,4)\n\nTutorial on Impure Haskell Programming\n62\n20\nlet gameState = GameState pTime 1\n21\nprint \"Guess a number between 1 and 4\"\n22\ngameLoop gameState\nYou notice in line 12 that since we are inside of a do expression we can lift (or unwrap) the IO String\n() value returned from getLine to a string value that we can use directly. This is a pattern we will\nuse repeatedly. The value returned from getLine is not used until line 13 when we use function read\nto extract the value from the IO String () value getLine returned.\nIn the if expression in lines 14-16 we check if the user has input the correct value and can then\nsimply return the input game state to the calling main function. If the user has not guessed the\ncorrect number then in line 16 we create a new game state value and call the function gameLoop\nrecursively with the newly constructed game state.\nThe following listing shows a sample session playing the number guessing game.\nPrelude> :l GameLoop2.hs\n[1 of 1] Compiling GameLoop2\n( GameLoop2.hs, interpreted )\nOk, modules loaded: GameLoop2.\n*GameLoop2> main\n\"Guess a number between 1 and 4\"\nEnter a number:\n1\nEnter a number:\n3\nEnter a number:\n4\nGameState {numberToGuess = 4, numTries = 3}\n*GameLoop2> main\n\"Guess a number between 1 and 4\"\nEnter a number:\n1\nEnter a number:\n2\nGameState {numberToGuess = 2, numTries = 2}\n*GameLoop2>\nWe will use this pattern for maintaining state in a game in the later chapter “Haskell Program to\nPlay the Blackjack Card Game.”\nEfficiency of Haskell Strings\nExcept for the Client/Server example, so far we have been mostly using simple String values where\nString is a list of characters [Char]. For longer strings it is much more efficient to use the module\n\nTutorial on Impure Haskell Programming\n63\nData.Text²² that is defined in package text (so text needs to be added to the dependencies in your\ncabal file).\nMany Haskell libraries use the simple String type but the use of Data.Text is also common, especially\nin applications handling large amounts of string data. We have already seen examples of this in the\nclient/server example programs. Fortunately Haskell is a strongly typed language that supports a\nlanguage extension for automatically handling both simple strings and the more efficient text types.\nThis language extension, as we have seen in a previous example, is activated by adding the following\nnear the top of a Haskell source file:\n{-# LANGUAGE OverloadedStrings\n#-}\nAs much as possible I am going to use simple strings in this book and when we need both simple\nstrings and byte strings I will then use OverloadedStrings for automatic conversion. This conversion\nis performed by knowing the type signatures of data and functions in surrounding code. The compiler\nfigures out what type of string is expected and does the conversion for you.\nA More Detailed Look at Monads\nWe have been casually using different types of IO () monads. In this section I will introduce you to\nthe State monad and then we will take a deeper look at IO (). While we will be just skimming the\nsurface of the topic of monads, my goal in this section is to teach you enough to work through the\nremaining examples in this book.\nMonads are types belonging to the Monad type class that specifies one operator and one function:\nclass Monad m where\n(>>=) :: m a -> (a -> m b) -> m b\nreturn :: a -> m a\nThe >>= operator takes two arguments: a monad wrapping a value (type a in the above listing) and\na function taking the same type a and returning a monad wrapping a new type b. The return value\nof >>= is a new monad wrapping a value of type b.\nThe Monad type class function return takes any value and wraps it in a new monad. The naming\nof return is confusing because it does not alter the flow of execution in a program like a return\nstatement in Java, rather, it wraps a value in a monad.\nState Monad\nThe definition for the constructor of a State monad is:\n²²https://www.stackage.org/nightly-2016-09-18/package/text-1.2.2.1\n\nTutorial on Impure Haskell Programming\n64\nnewtype State s a = State { runState :: s -> (a, s) }\nSo far we have been using data to define new types and newtype is similar except newtype acts\nduring compile time and no type information is present at runtime. All monads contain a value and\nfor the State monad this value is a function. The >>= operator is called the bind operator.\nThe accessor function runState provides the means to access the value in the state. The following\nexample is in the file StateMonad/State1.hs. In this example, incrementState is a state monad that\nincreases its wrapped integer value by one when it is executed. Remember that the return function\nis perhaps poorly named because it does not immediately “return” from a computation block as it\ndoes in other languages; return simply wraps a value as a monad without redirecting the execution\nflow.\nIn order to make the following example more clear, I implement the increment state function twice,\nonce using the do notation that you are already familiar with and once using the >>= bind operator:\n1\nmodule Main where\n2\n3\nimport Control.Monad.State\n4\n5\nincrementState :: State Int Int\n6\nincrementState = do\n7\nn <- get\n8\nput (n + 1)\n9\nreturn n\n10\n11\n-- same state monad without using a 'do' expression:\n12\nincrementState2 :: State Int Int\n13\nincrementState2 = get >>= \\a ->\n14\nput (a + 1) >>= \\b ->\n15\nreturn a\n16\n17\nbumpVals (a,b) = (a+1, b+2)\n18\n19\nmain = do\n20\nprint $ runState incrementState 1\n-- (1,2) == (return value, final state)\n21\nprint $ runState incrementState2 1 -- (1,2) == (return value, final state)\n22\nprint $ runState (mapState bumpVals incrementState) 1 -- (2,4)\n23\nprint $ evalState incrementState 1\n-- 1 == return value\n24\nprint $ execState incrementState 1\n-- 2 == final state\nHere we have used two very different looking, yet equivalent, styles for accessing and modifying\nstate monad values. In lines 6-9 we are using the do notation. The function get in line 7 returns one\nvalue: the value wrapped in a state monad. Function put in line 8 replaces the wrapped value in the\n\nTutorial on Impure Haskell Programming\n65\nstate monad, in this example by incrementing its numeric value. Finally return wraps the value in\na monad.\nI am using the runState function defined in lines 20-24 that returns a tuple: the first tuple value is\nthe result of the computation performed by the function passed to runState (incrementState and\nincrementState2 in these examples) and the second tuple value is the final wrapped state.\nIn lines 12-15 I reimplemented increment state using the bind function (>>=). We have seen before\nthat >>= passes the value on its left side to the computation on its right side, that is function calls in\nlines 13-15:\n\\a -> put (a + 1)\n\\b -> return a\nIt is a matter of personal taste whether to code using bind or do. I almost always use the do notation\nin my own code but I wanted to cover bind both in case you prefer that notation and so you can\nalso read and understand Haskell code using bind. We continue looking at alternatives to the do\nnotation in the next section.\nUsing Applicative Operators <$> and <*>: Finding\nCommon Words in Files\nMy goal in this book is to show you a minimal subset of Haskell that is relatively easy to understand\nand use for coding. However, a big part of using a language is reading other people’s code so I do\nneed to introduce a few more constructs that are widely used: applicative operators.\nBefore we begin I need to introduce you to a new term: Functor which is a typeclass that defines\nonly one method fmap. fmap is used to map a function over an IO action and has the type signature:\nfmap :: Functor f => (a -> b) -> f a -> f b\nfmap can be used to apply a pure function like (a -> b) to an IO a and return a new IO b without\nunwrapping the original IO (). The following short example (in file ImPure/FmapExample.hs) will\nlet you play with this idea:\n\nTutorial on Impure Haskell Programming\n66\n1\nmodule FmapExample where\n2\n3\nfileToWords fileName = do\n4\nfileText <- readFile fileName\n5\nreturn $ words fileText\n6\n7\nmain = do\n8\nwords1 <- fileToWords \"text1.txt\"\n9\nprint $ reverse words1\n10\nwords2 <- fmap reverse $ fileToWords \"text1.txt\"\n11\nprint words2\nIn lines 8-9 I am unwrapping the result of the IO [String] returned by the function fileToWords\nand then applying the pure function words to the unwrapped value. Wouldn’t it be nice to operate\non the words in the file without unwrapping the [String] value? You can do this using fmap as seen\nin lines 10-11. Please take a moment to understand what line 10 is doing. Here is line 10:\nwords2 <- fmap reverse $ fileToWords \"text1.txt\"\nFirst we read the words in a file into an IO [String] monad:\nfileToWords \"text1.txt\"\nThen we apply the pure function reverse to the values inside the IO [String] monad, creating a new\ncopy:\nfmap reverse $ fileToWords \"text1.txt\"\nNote that from the type of the fmap function, the input monad and output monad can wrap different\ntypes. For example, if we applied the function head to an IO [String] we would get an outut of IO\n[Char].\nFinally we unwrap the [String] value inside the monad and set words2 to this unwrapped value:\nwords2 <- fmap reverse $ fileToWords \"text1.txt\"\nIn summary, the Functor typeclass defines one method fmap that is useful for operating on data\nwrapped inside a monad.\nWe will now implement a small application that finds common words in two text files, implementing\nthe primary function three times, using:\n• The do notation.\n• The >>= bind operator.\n• The Applicative operators <$> and <*>\nLet’s look at the types for these operators:\n\nTutorial on Impure Haskell Programming\n67\n(<$>) :: Functor f => (a -> b) -> f a -> f b\n(<*>) :: Applicative f => f (a -> b) -> f a -> f b\nWe will use both <$> and <*> in the function commonWords3 in this example and I will explain\nhow these operators work after the following program listing.\nThis practical example will give you a chance to experiment more with Haskell (you do have a GHCi\nrepl open now, right?). The source file for this example is in the file ImPure/CommonWords.hs:\n1\nmodule CommonWords where\n2\n3\nimport Data.Set (fromList, toList, intersection)\n4\nimport Data.Char (toLower)\n5\n6\nfileToWords fileName = do\n7\nfileText <- readFile fileName\n8\nreturn $ (fromList . words) (map toLower fileText)\n9\n10\ncommonWords file1 file2 = do\n11\nwords1 <- fileToWords file1\n12\nwords2 <- fileToWords file2\n13\nreturn $\ntoList $ intersection words1 words2\n14\n15\ncommonWords2 file1 file2 =\n16\nfileToWords file1 >>= \\f1 ->\n17\nfileToWords file2 >>= \\f2 ->\n18\nreturn $\ntoList $ intersection f1 f2\n19\n20\ncommonWords3 file1 file2 =\n21\n(\\f1 f2 -> toList $ intersection f1 f2)\n22\n<$> fileToWords file1\n23\n<*> fileToWords file2\n24\n25\nmain = do\n26\ncw <- commonWords \"text1.txt\" \"text2.txt\"\n27\nprint cw\n28\ncw2 <- commonWords \"text1.txt\" \"text2.txt\"\n29\nprint cw2\n30\ncw3 <- commonWords \"text1.txt\" \"text2.txt\"\n31\nprint cw3\nThe function fileToWords defined in lines 6-8 simply reads a file, as in the last example, maps\ncontents of the file to lower case, uses words to convert a String to a [String] list of individual\n\nTutorial on Impure Haskell Programming\n68\nwords, and uses the function Data.Set.fromList to create a set from a list of words that in general\nwill have duplicates. We are retuning an IO (Data.Set.Base.Set String) value so we can later perform\na set intersection operation. In other applications you might want to apply Data.Set.toList before\nreturning the value from fileToWords so the return type of the function would be IO [String].\nThe last listing defines three similar functions commonWords, commonWords2, and common-\nWords3.\ncommonWords defined in lines 10-13 should hopefully look routine and familiar to you now. We\nset the local variables with the unwrapped (i.e., extracted from a monad) contents of the unique\nwords in two files, and then return monad wrapping the intersection of the words in both files.\nThe function commonWords2 is really the same as commonWords except that it uses the bind >>=\noperator instead of the do notation.\nThe interesting function in this example is commonWords3 in lines 20-23 which uses the applicative\noperators <$> and <*>. Notice the pure function defined inline in line 21: it takes two arguments of\ntype set and returns the set intersection of the arguments. The operator <$> takes a function on\nthe left side and a monad on the right side which contains the wrapped value to be passed as the\nargument f1. <*> supplies the value for the inline function arguments f2. To rephrase how lines 21-23\nwork: we are calling fileToWords twice, both times getting a monad. These two wrapped monad\nvalues are passed as arguments to the inline function in line 21 and the result of evaluating this\ninline function is returned as the value of the function commonWords3.\nI hope that this example has at least provided you with “reading knowledge” of the Applicative\noperators <$> and <*> and has also given you one more example of replacing the do notation with\nthe use of the bind >>= operator.\nList Comprehensions Using the do Notation\nWe saw examples of list comprehensions in the last chapter on pure Haskell programming. We can\nuse return to get lists values that are instances of type Monad:\n*Prelude> :t (return [])\n(return []) :: Monad m => m [t]\n*Prelude> :t (return [1,2,3])\n(return [1,2,3]) :: (Monad m, Num t) => m [t]\n*Prelude> :t (return [\"the\",\"tree\"])\n(return [\"the\",\"tree\"]) :: Monad m => m [[Char]]\nWe can get list comprehension behavior from the do notation (here I am using the GHCi repl :{ and\n:} commands to enter multiple line examples):\n\nTutorial on Impure Haskell Programming\n69\n1\n*Main> :{\n2\n*Main| do num <- [1..3]\n3\n*Main|\nanimal <- [\"parrot\", \"ant\", \"dolphin\"]\n4\n*Main|\nreturn (num, animal)\n5\n*Main| :}\n6\n[(1,\"parrot\"),(1,\"ant\"),(1,\"dolphin\"),\n7\n(2,\"parrot\"),(2,\"ant\"),(2,\"dolphin\"),\n8\n(3,\"parrot\"),(3,\"ant\"),(3,\"dolphin\")]\nI won’t use this notation further but you now will recognize this pattern if you read it in other\npeople’s code.\nDealing With Time\nIn the example in this section we will see how to time a block of code (using two different methods)\nand how to set a timeout for code that runs in an IO ().\nThe first way we time a block of code uses getPOSIXTime and can be used to time pure or impure\ncode. The second method using timeIt takes an IO () as an argument; in the following example I\nwrapped pure code in a print function call which returns an IO () as its value. The last example in\nthe file TimerTest.hs shows how to run impure code wrapped in a timeout.\n1\nmodule Main where\n2\n3\nimport Data.Time.Clock.POSIX -- for getPOSIXTime\n4\nimport System.TimeIt\n-- for timeIt\n5\nimport System.Timeout\n-- for timeout\n6\n7\nanyCalculationWillDo n =\n-- a function that can take a while to run\n8\ntake n $ sieve [2..]\n9\nwhere\n10\nsieve (x:xs) =\n11\nx:sieve [y | y <- xs, rem y x > 0]\n12\n13\nmain = do\n14\nstartingTime <- getPOSIXTime\n15\nprint startingTime\n16\nprint $ last $ take 20000001 [0..]\n17\nendingTime <- getPOSIXTime\n18\nprint endingTime\n19\nprint (endingTime - startingTime)\n20\ntimeIt $ print $ last $ anyCalculationWillDo 2000\n\nTutorial on Impure Haskell Programming\n70\n21\n22\nlet somePrimes = anyCalculationWillDo 3333 in\n23\ntimeIt $ print $ last somePrimes\n24\n25\n-- 100000 microseconds timeout tests:\n26\ntimeout 100000 $ print \"simple print **do** expression did not timeout\"\n27\ntimeout 100000 $ print $ last $ anyCalculationWillDo 4\n28\ntimeout 100000 $ print $ last $ anyCalculationWillDo 40\n29\ntimeout 100000 $ print $ last $ anyCalculationWillDo 400\n30\ntimeout 100000 $ print $ last $ anyCalculationWillDo 4000\n31\ntimeout 100000 $ print $ last $ anyCalculationWillDo 40000\n32\nprint $ anyCalculationWillDo 5\nI wanted a function that takes a while to run so for anyCalculationWillDo (lines 7 to 11) I\nimplemented an inefficient prime number generator.\nWhen running this example on my laptop, the last two timeout calls (lines 26 and 31) are terminated\nfor taking more than 100000 microseconds to execute.\nThe last line 32 of code prints out the first 5 prime numbers greater than 1 so you can see the results\nof calling the time wasting test function anyCalculationWillDo.\n1\n$ stack build --exec TimerTest\n2\n1473610528.2177s\n3\n20000000\n4\n1473610530.218574s\n5\n2.000874s\n6\n17389\n7\nCPU time:\n0.14s\n8\n30911\n9\nCPU time:\n0.25s\n10\n\"simple print **do** expression did not timeout\"\n11\n7\n12\n173\n13\n2741\n14\n[2,3,5,7,11]\nThe timeout function is useful for setting a maximum time that you are willing to wait for a\ncalculation to complete. I mostly use timeout for timing out operations fetching data from the web.\nUsing Debug.Trace\nInside an IO you can use print statements to understand what is going on in your code when\ndebugging. You can not use print statements inside pure code but the Haskell base library contains\n\nTutorial on Impure Haskell Programming\n71\nthe trace functions that internally perform impure writes to stdout. You do not want to use these\ndebug tools in production code.\nAs an example, I have rewritten the example from the last section to use Debug.Trace.trace and\nDebug.Trace.traceShow:\n1\nmodule Main where\n2\n3\nimport Debug.Trace\n(trace, traceShow) -- for debugging only!\n4\n5\nanyCalculationWillDo n =\n6\ntrace\n7\n(\"+++ anyCalculationWillDo: \" ++ show n) $\n8\nanyCalculationWillDo' n\n9\n10\nanyCalculationWillDo' n =\n11\ntake n $ trace (\"\n-- sieve n:\" ++ (show n)) $ sieve [2..]\n12\nwhere\n13\nsieve (x:xs) =\n14\ntraceShow (\"\n-- inside sieve recursion\") $\n15\nx:sieve [y | y <- xs, rem y x > 0]\n16\n17\nmain = do\n18\nprint $ anyCalculationWillDo 5\nIn line 3 we import the trace and showTrace functions:\n*Main> :info trace\ntrace :: String -> a -> a\n-- Defined in ‘Debug.Trace’\n*Main> :info traceShow\ntraceShow :: Show a => a -> b -> b\n-- Defined in ‘Debug.Trace’\ntrace takes two arguments: the first is a string that that is written to stdout and the second is a\nfunction call to be evaluated. traceShow is like *trace except that the first argument is cnverted to\na tstring. The output from running this example is:\n\nTutorial on Impure Haskell Programming\n72\n+++ anyCalculationWillDo: 5\n-- sieve n:5\n\"\n-- inside sieve recursion\"\n\"\n-- inside sieve recursion\"\n\"\n-- inside sieve recursion\"\n\"\n-- inside sieve recursion\"\n\"\n-- inside sieve recursion\"\n[2,3,5,7,11]\nI don’t usually like using the trace functions because debugging with them involves slightly\nrewriting my code. My preference is to get low level code written interactively in the GHCI repl\nso it does not need to be debugged. I very frequently use print statement inside IOs since adding\nthem requires no significant modification of my code.\nWrap Up\nI tried to give you a general fast-start in this chapter for using monads and in general writing impure\nHaskell code. This chapter should be sufficient for you to be able to understand and experiment with\nthe examples in the rest of this book.\nThis is the end of the first section. We will now look at a variety of application examples using the\nHaskell language.\nWhile I expect you to have worked through the previous chapters in order, for the rest of the book\nyou can skip around and read the material in any order that you wish.\n\nSection 2 - Cookbook\nNow that you have worked through the pure and impure Haskell coding tutorials in the first two\nchapters we will look at a “cookbook” of techniques and sample applications to solve some common\nprogramming tasks as well as implement a program to play the card game Blackjack.\nI expect you, dear reader, to have studied and absorbed the tutorial material on pure and impure\nHaskell programming in the first two chapters. If you are new to Haskell, or don’t have much\nexperience yet, carefully working through these tutorial chapters is a requirement for understanding\nthe material in the rest of this book.\nThis section contains the following “recipe” applications:\n• Textprocessing CSV Files\n• Textprocessing JSON Files\n• Using sqlite and Postgres databases\n• REST Server Providing JSON Data\n• REST Client\n• Accessing and Using Linked Data\n• Querying Semantic Web RDF Data Sources\n• Annotating English text with Wikipedia/DBPedia URIs for entities in the original text. Entities\ncan be people, places, organizations, etc.\n• Play the Blackjack card game\n• Machine Learning\n• Probabilistic Graph Models\n\nText Processing\nIn my work in data science and machine learning, processing text is a core activity. I am a practitioner,\nnot a research scientist, and in a practical sense, I spend a fair amount of time collecting data (e.g.,\nweb scraping and using semantic web/linked data sources), cleaning it, and converting it to different\nformats.\nWe will cover three useful techniques: parsing and using CSV (comma separated values) spreadsheet\nfiles, parsing and using JSON data, and cleaning up natural language text that contains noise\ncharacters.\nCSV Spreadsheet Files\nThe comma separated values (CSV) format is a plain text format that all spreadsheet applications\nsupport. The following example illustrates two techniques that we haven’t covered yet:\n• Extracting values from the Either type.\n• Using destructuring to concisely extract parts of a list.\nThe Either type Either a b contains either a Left a or a Right b value and is usually used to return\nan error in Left or a value in Right. We will using the Data.Either.Unwrap module to unwrap the\nRight part of a call to the Text.CSV.parseCSVFromFile function that reads a CSV file and returns\na Left error or the data in the spreadsheet in a list as the Right value.\nThe destructuring trick in line 15 in the following listing lets us separate the head and rest of a list\nin one operation; for example:\n*TestCSV> let z = [1,2,3,4,5]\n*TestCSV> z\n[1,2,3,4,5]\n*TestCSV> let x:xs = z\n*TestCSV> x\n1\n*TestCSV> xs\n[2,3,4,5]\nHere is how to read a CSV file:\n\nText Processing\n75\n1\nmodule TestCSV where\n2\n3\nimport Text.CSV (parseCSVFromFile, CSV)\n4\nimport Data.Either.Unwrap (fromRight)\n5\n6\nreadCsvFile :: FilePath -> CSV\n7\nreadCsvFile fname = do\n8\nc <- parseCSVFromFile fname\n9\nreturn $ fromRight c\n10\n11\nmain = do\n12\nc <- readCsvFile \"test.csv\"\n13\nprint\nc\n-- includes header and data rows\n14\nprint $ map head c\n-- print header\n15\nlet header:rows = c -- destructure\n16\nprint header\n17\nprint rows\nFunction readCsvFile reads from a file and returns a CSV. What is a CSV type? You could search\nthe web for documentation, but dear reader, if you have worked this far learning Haskell, by now\nyou know to rely on the GHCi repl:\n*TestCSV> :i CSV\ntype CSV = [Text.CSV.Record]\n-- Defined in ‘Text.CSV’\n*TestCSV> :i Text.CSV.Record\ntype Text.CSV.Record = [Text.CSV.Field]\n-- Defined in ‘Text.CSV’\n*TestCSV> :i Text.CSV.Field\ntype Text.CSV.Field = String\n-- Defined in ‘Text.CSV’\nSo, a CSV is a list of records (rows in the spreadsheet file), each record is a list of fields (i.e., a string\nvalue).\nThe output when reading the CVS file test.csv is:\n\nText Processing\n76\nPrelude> :l TestCSV\n[1 of 1] Compiling TestCSV\n( TestCSV.hs, interpreted )\nOk, modules loaded: TestCSV.\n*TestCSV> main\n[[\"name\",\" email\",\" age\"],[\"John Smith\",\" jsmith@acmetools.com\",\" 41\"],[\"June Jones\"\\\n,\" jj@acmetools.com\",\" 38\"]]\n[\"name\",\"John Smith\",\"June Jones\"]\n[\"name\",\" email\",\" age\"]\n[[\"John Smith\",\" jsmith@acmetools.com\",\" 41\"],[\"June Jones\",\" jj@acmetools.com\",\" 38\\\n\"]]\nJSON Data\nJSON is the native data format for the Javascript language and JSON has become a popular\nserialization format for exchanging data between programs on a network. In this section I will\ndemonstrate serializing a Haskell type to a string with JSON encoding and then perform the opposite\noperation of deserializing a string containing JSON encoded data back to an object.\nThe first example uses the module Text.JSON.Generic (from the json library) and the second\nexample uses module Data.Aeson (from the aeson library).\nIn the first example, we set the language type to include DeriveDataTypeable so a new type definition\ncan simply derive Typeable which allows the compiler to generate appropriate encodeJSON and\ndecodeJSON functions for the type Person we define in the example:\n1\n{-# LANGUAGE DeriveDataTypeable #-}\n2\n3\nmodule TestTextJSON where\n4\n5\nimport Text.JSON.Generic\n6\n7\ndata Person = Person {name::String, email::String}\n8\nderiving (Show, Data, Typeable)\n9\n10\nmain = do\n11\nlet a = encodeJSON $ Person \"Sam\" \"sam@a.com\"\n12\nprint a\n13\nlet d = (decodeJSON a :: Person)\n14\nprint d\n15\nprint $ name d\n16\nprint $ email d\n\nText Processing\n77\nNotice that in line 14 that I specified the expected type in the decodeJSON call. This is not strictly\nrequired, the Haskell GHC compiler knows what to do in this case. I specified the type for code\nreadability. The Haskell compiler wrote the name and email functions for me and I use these\nfunctions in lines 16 and 17 to extract these fields. Here is the output from running this example:\n1\nPrelude> :l TestTextJSON.hs\n2\n[1 of 1] Compiling TestTextJSON\n( TestTextJSON.hs, interpreted )\n3\nOk, modules loaded: TestTextJSON.\n4\n*TestTextJSON> main\n5\n\"{\\\"name\\\":\\\"Sam\\\",\\\"email\\\":\\\"sam@a.com\\\"}\"\n6\nPerson {name = \"Sam\", email = \"sam@a.com\"}\n7\n\"Sam\"\n8\n\"sam@a.com\"\nThe next example uses the Aeson library and is similar to this example.\nUsing Aeson, we set a language type DeriveGeneric and in this case have the Person class derive\nGeneric. The School of Haskell has an excellent Aeson tutorial²³ that shows a trick I use in this\nexample: letting the compiler generate required functions for types FromJSON and ToJSON as seen\nin lines 12-13.\n1\n{-# LANGUAGE DeriveGeneric #-}\n2\n3\nmodule TestJSON where\n4\n5\nimport Data.Aeson\n6\nimport GHC.Generics\n7\nimport Data.Maybe\n8\n9\ndata Person = Person {name::String, email::String } deriving (Show, Generic)\n10\n11\n-- nice trick from School Of Haskell tutorial on Aeson:\n12\ninstance FromJSON Person\n-- DeriveGeneric language setting allows\n13\ninstance ToJSON Person\n-- automatic generation of instance of\n14\n-- types deriving Generic.\n15\n16\nmain = do\n17\nlet a = encode $ Person \"Sam\" \"sam@a.com\"\n18\nprint a\n19\nlet (Just d) = (decode a :: Maybe Person)\n20\nprint d\n21\nprint $ name d\n22\nprint $ email d\n²³https://www.schoolofhaskell.com/school/starting-with-haskell/libraries-and-frameworks/text-manipulation/json\n\nText Processing\n78\nI use a short cut in line 19, assuming that the Maybe object returned from decode (which the\ncompiler wrote automatically for the type FromJSON) contains a Just value instead of an empty\nNothing value. So in line 19 I directly unwrap the Just value.\nHere is the output from running this example:\n1\nPrelude> :l TestAESON.hs\n2\n[1 of 1] Compiling TestJSON\n( TestAESON.hs, interpreted )\n3\nOk, modules loaded: TestJSON.\n4\n*TestJSON> main\n5\n\"{\\\"email\\\":\\\"sam@a.com\\\",\\\"name\\\":\\\"Sam\\\"}\"\n6\nPerson {name = \"Sam\", email = \"sam@a.com\"}\n7\n\"Sam\"\n8\n\"sam@a.com\"\nLine 5 shows the result of printing the JSON encoded string value created by the call to encode in\nline 17 of the last code example. Line 6 shows the decoded value of type Person, and lines 7 and 8\nshow the inner wrapped values in the Person data.\nCleaning Natural Language Text\nI spend a lot of time working with text data because I have worked on NLP (natural language\nprocessing) projects for over 25 years. We will jump into some interesting NLP applications in the\nnext chapter. I will finish this chapter with strategies for cleaning up text which is often a precursor\nto performing NLP.\nYou might be asking why we would need to clean up text. Here are a few common use cases:\n• Text fetched from the web frequently contains garbage characters.\n• Some types of punctuation need to be removed.\n• Stop words (e.g., the, a, but, etc.) need to be removed.\n• Special unicode characters are not desired.\n• Sometimes we want white space around punctuation to make tokenizing text easier.\nNotice the module statement on line 1 of the following listing: I am exporting functions cleanText\nand removeStopWords so they will be visible and available for use by any other modules that import\nthis module. In line 6 we import intercalate which constructs a string from a space character and an\n[String] (i.e., a list of strings); here is an example where instead of adding a space character between\nthe strings joined together, I add “*” characters:\n\nText Processing\n79\n*CleanText> intercalate \"*\" [\"the\", \"black\", \"cat\"]\n\"the*black*cat\"\nThe function cleanText removes garbage characters and makes sure that any punctuation characters\nare surrounded by white space (this makes it easier, for example, to determine sentence boundaries).\nFunction removeStopWords removes common words like “a”, “the”, etc. from text.\n1\nmodule CleanText (cleanText, removeStopWords)\nwhere\n2\n3\nimport Data.List.Split (splitOn)\n4\nimport Data.List (intercalate)\n5\nimport Data.Char as C\n6\nimport Data.List.Utils (replace)\n7\n8\nnoiseCharacters = ['[', ']', '{', '}', '\\n', '\\t', '&', '^',\n9\n'@', '%', '$', '#', ',']\n10\n11\nsubstituteNoiseCharacters :: [Char] -> [Char]\n12\nsubstituteNoiseCharacters =\n13\nmap (\\x -> if elem x noiseCharacters then ' ' else x)\n14\n15\ncleanText s =\n16\nintercalate\n17\n\" \" $\n18\nfilter\n19\n(\\x -> length x > 0) $\n20\nsplitOn \" \" $ substituteNoiseCharacters $\n21\n(replace \".\" \" . \"\n22\n(replace \",\" \" , \"\n23\n(replace \";\" \" ; \" s)))\n24\n25\nstopWords = [\"a\", \"the\", \"that\", \"of\", \"an\"]\n26\n27\ntoLower' :: [Char] -> [Char]\n28\ntoLower' s = map (\\x -> if isLower x then x else (C.toLower x)) s\n29\n30\nremoveStopWords :: String -> [Char]\n31\nremoveStopWords s =\n32\nintercalate\n33\n\" \" $\n34\nfilter\n35\n(\\x -> notElem (toLower' x) stopWords) $\n36\nwords s\n\nText Processing\n80\n37\n38\nmain = do\n39\nlet ct = cleanText \"The[]@] cat, and all dog, escaped&^. They were caught.\"\n40\nprint ct\n41\nlet nn = removeStopWords ct\n42\nprint nn\nThis example should be extended with additional noise characters and stop words, depending on\nyour application. The function cleanText simply uses substring replacements.\nLet’s look more closely at removeStopWords that takes a single argument s, which is expected to\nbe a string. removeStopWords uses a combination of several functions to remove stop words from\nthe input string. The function words is used to split the input string s into a list of words. Then, the\nfunction filter is used to remove any words that match a specific condition. Here the condition is\ndefined as a lambda function, which is passed as the first argument to the filter function. The lambda\nfunction takes a single argument x and returns a Boolean value indicating whether the word should\nbe included in the output or not. The lambda function uses function notElem to check whether the\nlowercased version of the word x is present in a predefined list of stop words. Finally, we use the\nfunction intercalate to join the remaining words back into a single string. The first argument to\nfunction ** intercalate** is the separator that should be used to join the words, in this case, it’s a\nsingle space.\nHere is the output from this example:\n1\n*TestCleanText> :l CleanText.hs\n2\n[1 of 1] Compiling TestCleanText\n( CleanText.hs, interpreted )\n3\nOk, modules loaded: TestCleanText.\n4\n*TestCleanText> main\n5\n\"The cat and all dog escaped . They were caught .\"\n6\n\"cat dog escaped . They were caught .\"\nWe will continue working with text in the next chapter.\n\nNatural Language Processing Tools\nThe tools developed in this chapter are modules you can reuse in your programs. We will develop\na command line program that reads a line of text from STDIN and writes sematic information as\noutput to STDOUT. I have used this in a Ruby program by piping input text data to a forked process\nand reading the output which is a semantic representation of the input text.\nWe will be using this example as an external dependency to a later example in the chapter\nKnowledge Graph Creator.\nA few of the data files I provide in this example are fairly large. As an example the file PeopleDb-\nPedia.hs which builds a map from people’s names to the Wikipedia/DBPedia URI for information\nabout them, is 2.5 megabytes in size. The first time you run stack build in the project directory it\nwill take a while, so you might want to start building the project in the directory NlpTool and let it\nrun while you read this chapter.\nHere are three examples using the NlpTool command line application developed in this chapter:\nEnter text (all on one line)\nCanada and England signed a trade deal.\ncategory:\neconomics\nsummary:\nCanada and England signed a trade deal.\ncountries:\n[[\"Canada\",\"<http://dbpedia.org/resource/Canada>\"],\n[\"England\",\"<http://dbpedia.org/resource/England>\"]]\nEnter text (all on one line)\nPresident George W Bush asked Congress for permission to invade Iraq.\ncategory:\nnews_war\nsummary:\nPresident George W Bush asked Congress for permission to invade Iraq.\npeople:\n[[\"George W Bush\",\"<http://dbpedia.org/resource/George_W._Bush>\"]]\ncountries:\n[[\"Iraq\",\"\"]]\nEnter text (all on one line)\nThe British government is facing criticism from business groups over statements sugg\\\nesting the U.K. is heading for a hard divorce from the European Union â€” and pressu\\\nre from lawmakers who want Parliament to have a vote on the proposed exit terms. The\\\ngovernment's repeated emphasis on controlling immigration sent out \"signs that the \\\ndoor is being closed, to an extent, on the open economy, that has helped fuel invest\\\nment,\" the head of employers' group the Confederation of British Industry, Carolyn F\\\nairbairn, said in comments published Monday. Prime Minister Theresa May said last we\\\nek that Britain would seek to retain a close relationship with the 28-nation bloc, w\\\nith continued free trade in goods and services. But she said the U.K. wouldn't cede \\\ncontrol over immigration, a conflict with the EU's principle of free movement among \\\n\nNatural Language Processing Tools\n82\nmember states.\ncategory:\neconomics\nsummary:\nPrime Minister Theresa May said last week that Britain would seek to retain\\\na close relationship with the 28-nation bloc, with continued free trade in goods an\\\nd services.\ncredit: news text from abcnews.com\nResolve Entities in Text to DBPedia URIs\nThe code for this application is in the directory NlpTool.\nThe software and data in this chapter can be used under the terms of either the GPL version 3 license\nor the Apache 2 license.\nThere are several automatically generated Haskell formatted data files that I created using Ruby\nscripts operating the Wikipedia data. For the purposes of this book I include these data-specific files\nfor your use and enjoyment but we won’t spend much time discussing them. These files are:\n• BroadcastNetworkNamesDbPedia.hs\n• CityNamesDbpedia.hs\n• CompanyNamesDbpedia.hs\n• CountryNamesDbpedia.hs\n• PeopleDbPedia.hs\n• PoliticalPartyNamesDbPedia.hs\n• TradeUnionNamesDbPedia.hs\n• UniversityNamesDbPedia.hs\nAs an example, let’s look at a small sample of data in PeopleDbPedia.hs:\n1\nmodule PeopleDbPedia (peopleMap) where\n2\n3\nimport qualified Data.Map as M\n4\n5\npeopleMap = M.fromList [\n6\n(\"Aaron Sorkin\", \"<http://dbpedia.org/resource/Aaron_Sorkin>\"),\n7\n(\"Bill Clinton\", \"<http://dbpedia.org/resource/Bill_Clinton>\"),\n8\n(\"George W Bush\", \"<http://dbpedia.org/resource/George_W_Bush>\"),\nThere are 35,146 names in the file PeopleDbPedia.hs. I have built for eight different types of entity\nnames: Haskell maps that take entity names (String) and maps the entity names into relevant\n\nNatural Language Processing Tools\n83\nDBPedia URIs. Simple in principle, but a lot of work preparing the data. As I mentioned, we will use\nthese data-specific files to resolve entity references in text.\nThe next listing shows the file Entities.hs. In lines 5-7 I import the entity mapping files I just described.\nIn this example and later code I make heavy use of the Data.Map and Data.Set modules in the\ncollections library (see the NlpTools.cabal file).\nThe operator isSubsetOf defined in line 39 tests to see if a value is contained in a collection. The\nbuilt-in function all applies a function or operator to all elements in a collection and returns a true\nvalue if the function or operator returns true applied to each element in the collection.\nThe local utility function namesHelper defined in lines 41-53 is simpler than it looks. The function\nfilter in line 42 applies the inline function in lines 43-45 (this function returns true for Maybe values\nthat contain data) to a second list defined in lines 48-55. This second list is calculated by mapping\nan inline function over the input argument ngrams. The inline function looks up an ngram in a\nDBPedia map (passed as the second function argument) and returns the lookup value if it is not\nempty and if it is empty looks up the same ngram in a word map (last argument to this function).\nThe utility function namesHelper is then used to define functions to recognize company names,\ncountry names, people names, city names, broadcast network names, political party names, trade\nunion names, and university names:\n1\n-- Copyright 2014 by Mark Watson. All rights reserved. The software and data in this\\\n2\nproject can be used under the terms of either the GPL version 3 license or the Apac\\\n3\nhe 2 license.\n4\n5\nmodule Entities (companyNames, peopleNames,\n6\ncountryNames, cityNames, broadcastNetworkNames,\n7\npoliticalPartyNames, tradeUnionNames, universityNames) where\n8\n9\nimport qualified Data.Map as M\n10\nimport qualified Data.Set as S\n11\nimport Data.Char (toLower)\n12\nimport Data.List (sort, intersect, intersperse)\n13\nimport Data.Set (empty)\n14\nimport Data.Maybe (isJust)\n15\n16\nimport Utils (splitWords, bigram, bigram_s, splitWordsKeepCase,\n17\ntrigram, trigram_s, removeDuplicates)\n18\n19\nimport FirstNames (firstNames)\n20\nimport LastNames (lastNames)\n21\nimport NamePrefixes (namePrefixes)\n22\n23\nimport PeopleDbPedia (peopleMap)\n\nNatural Language Processing Tools\n84\n24\n25\nimport CountryNamesDbpedia (countryMap)\n26\nimport CountryNames (countryNamesOneWord, countryNamesTwoWords, countryNamesThreeWor\\\n27\nds)\n28\n29\nimport CompanyNamesDbpedia (companyMap)\n30\nimport CompanyNames (companyNamesOneWord, companyNamesTwoWords, companyNamesThreeWor\\\n31\nds)\n32\nimport CityNamesDbpedia (cityMap)\n33\n34\nimport BroadcastNetworkNamesDbPedia (broadcastNetworkMap)\n35\nimport PoliticalPartyNamesDbPedia (politicalPartyMap)\n36\nimport TradeUnionNamesDbPedia (tradeUnionMap)\n37\nimport UniversityNamesDbPedia (universityMap)\n38\n39\nxs `isSubsetOf` ys = all (`elem` ys) xs\n40\n41\nnamesHelper ngrams dbPediaMap wordMap =\n42\nfilter\n43\n(\\x -> case x of\n44\n(_, Just x) -> True\n45\n_ -> False) $\n46\nmap (\\ngram -> (ngram,\n47\nlet v = M.lookup ngram dbPediaMap in\n48\nif isJust v\n49\nthen return (ngram, v)\n50\nelse if S.member ngram wordMap\n51\nthen Just (ngram, Just \"\")\n52\nelse Nothing))\n53\nngrams\n54\n55\nhelperNames1W = namesHelper\n56\n57\nhelperNames2W wrds = namesHelper (bigram_s wrds)\n58\n59\nhelperNames3W wrds =\nnamesHelper (trigram_s wrds)\n60\n61\ncompanyNames wrds =\n62\nlet cns = removeDuplicates $ sort $\n63\nhelperNames1W wrds companyMap companyNamesOneWord ++\n64\nhelperNames2W wrds companyMap companyNamesTwoWords ++\n65\nhelperNames3W wrds companyMap companyNamesThreeWords in\n66\nmap (\\(s, Just (a,Just b)) -> (a,b)) cns\n\nNatural Language Processing Tools\n85\n67\n68\ncountryNames wrds =\n69\nlet cns = removeDuplicates $ sort $\n70\nhelperNames1W wrds countryMap countryNamesOneWord ++\n71\nhelperNames2W wrds countryMap countryNamesTwoWords ++\n72\nhelperNames3W wrds countryMap countryNamesThreeWords in\n73\nmap (\\(s, Just (a,Just b)) -> (a,b)) cns\n74\n75\npeopleNames wrds =\n76\nlet cns = removeDuplicates $ sort $\n77\nhelperNames1W wrds peopleMap Data.Set.empty ++\n78\nhelperNames2W wrds peopleMap Data.Set.empty ++\n79\nhelperNames3W wrds peopleMap Data.Set.empty in\n80\nmap (\\(s, Just (a,Just b)) -> (a,b)) cns\n81\n82\ncityNames wrds =\n83\nlet cns = removeDuplicates $ sort $\n84\nhelperNames1W wrds cityMap Data.Set.empty ++\n85\nhelperNames2W wrds cityMap Data.Set.empty ++\n86\nhelperNames3W wrds cityMap Data.Set.empty in\n87\nmap (\\(s, Just (a,Just b)) -> (a,b)) cns\n88\n89\nbroadcastNetworkNames wrds =\n90\nlet cns = removeDuplicates $ sort $\n91\nhelperNames1W wrds broadcastNetworkMap Data.Set.empty ++\n92\nhelperNames2W wrds broadcastNetworkMap Data.Set.empty ++\n93\nhelperNames3W wrds broadcastNetworkMap Data.Set.empty in\n94\nmap (\\(s, Just (a,Just b)) -> (a,b)) cns\n95\n96\npoliticalPartyNames wrds =\n97\nlet cns = removeDuplicates $ sort $\n98\nhelperNames1W wrds politicalPartyMap Data.Set.empty ++\n99\nhelperNames2W wrds politicalPartyMap Data.Set.empty ++\n100\nhelperNames3W wrds politicalPartyMap Data.Set.empty in\n101\nmap (\\(s, Just (a,Just b)) -> (a,b)) cns\n102\n103\ntradeUnionNames wrds =\n104\nlet cns = removeDuplicates $ sort $\n105\nhelperNames1W wrds tradeUnionMap Data.Set.empty ++\n106\nhelperNames2W wrds tradeUnionMap Data.Set.empty ++\n107\nhelperNames3W wrds tradeUnionMap Data.Set.empty in\n108\nmap (\\(s, Just (a,Just b)) -> (a,b)) cns\n109\n\nNatural Language Processing Tools\n86\n110\nuniversityNames wrds =\n111\nlet cns = removeDuplicates $ sort $\n112\nhelperNames1W wrds universityMap Data.Set.empty ++\n113\nhelperNames2W wrds universityMap Data.Set.empty ++\n114\nhelperNames3W wrds universityMap Data.Set.empty in\n115\nmap (\\(s, Just (a,Just b)) -> (a,b)) cns\n116\n117\n118\nmain = do\n119\nlet s = \"As read in the San Francisco Chronicle, the company is owned by John Sm\\\n120\nith, Bill Clinton, Betty Sanders, and Dr. Ben Jones. Ben Jones and Mr. John Smith ar\\\n121\ne childhood friends who grew up in Brazil, Canada, Buenos Aires, and the British Vir\\\n122\ngin Islands. Apple Computer relased a new version of OS X yesterday. Brazil Brazil B\\\n123\nrazil. John Smith bought stock in ConocoPhillips, Heinz, Hasbro, and General Motors,\\\n124\nFox Sports Radio. I listen to B J Cole. Awami National Party is a political party. \\\n125\nALAEA is a trade union. She went to Brandeis University.\"\n126\n--print $ humanNames s\n127\nprint $ peopleNames $ splitWordsKeepCase s\n128\nprint $ countryNames $ splitWordsKeepCase s\n129\nprint $ companyNames $ splitWordsKeepCase s\n130\nprint $ cityNames $ splitWordsKeepCase s\n131\nprint $ broadcastNetworkNames $ splitWordsKeepCase s\n132\nprint $ politicalPartyNames $ splitWordsKeepCase s\n133\nprint $ tradeUnionNames $ splitWordsKeepCase s\n134\nprint $ universityNames $ splitWordsKeepCase s\nThe following output is generated by running the test main function defined at the bottom of the\nfile app/NlpTool.hs:\n1\n$ stack build --fast --exec NlpTool-exe\n2\nBuilding all executables for `NlpTool' once. After a successful build of all of them\\\n3\n, only specified executables will be rebuilt.\n4\nNlpTool> build (lib + exe)\n5\nPreprocessing library for NlpTool-0.1.0.0..\n6\nBuilding library for NlpTool-0.1.0.0..\n7\nPreprocessing executable 'NlpTool-exe' for NlpTool-0.1.0.0..\n8\nBuilding executable 'NlpTool-exe' for NlpTool-0.1.0.0..\n9\n[1 of 2] Compiling Main\n10\n[2 of 2] Compiling Paths_NlpTool\n11\nLinking .stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build/NlpTool-exe/NlpTool-exe ...\n12\nNlpTool> copy/register\n13\nInstalling library in /Users/markw/GITHUB/haskell_tutorial_cookbook_examples_private\\\n14\n_new_edition/NlpTool/.stack-work/install/x86_64-osx/7a2928fbf8188dcb20f165f77b37045a\\\n\nNatural Language Processing Tools\n87\n15\n5c413cc7f63913951296700a6b7e292d/8.6.5/lib/x86_64-osx-ghc-8.6.5/NlpTool-0.1.0.0-DXKb\\\n16\nucyA0S0AKOAcZGDl2H\n17\nInstalling executable NlpTool-exe in /Users/markw/GITHUB/haskell_tutorial_cookbook_e\\\n18\nxamples_private_new_edition/NlpTool/.stack-work/install/x86_64-osx/7a2928fbf8188dcb2\\\n19\n0f165f77b37045a5c413cc7f63913951296700a6b7e292d/8.6.5/bin\n20\nRegistering library for NlpTool-0.1.0.0..\n21\nEnter text (all on one line)\n22\nAs read in the San Francisco Chronicle, the company is owned by John Smith, Bill Cli\\\n23\nnton, Betty Sanders, and Dr. Ben Jones. Ben Jones and Mr. John Smith are childhood f\\\n24\nriends who grew up in Brazil, Canada, Buenos Aires, and the British Virgin Islands. \\\n25\nApple Computer relased a new version of OS X yesterday. Brazil Brazil Brazil. John S\\\n26\nmith bought stock in ConocoPhillips, Heinz, Hasbro, and General Motors, Fox Sports R\\\n27\nadio. I listen to B J Cole. Awami National Party is a political party. ALAEA is a tr\\\n28\nade union. She went to Brandeis University.\n29\ncategory:\nnews_politics\n30\nsummary:\nALAEA is a trade union. Apple Computer relased a new version of OS X yester\\\n31\nday.\n32\npeople:\n[[\"B J Cole\",\"<http://dbpedia.org/resource/B._J._Cole>\"]]\n33\ncompanies:\n[[\"Apple\",\"<http://dbpedia.org/resource/Apple>\"],[\"ConocoPhillips\",\"<http\\\n34\n://dbpedia.org/resource/ConocoPhillips>\"],[\"Hasbro\",\"<http://dbpedia.org/resource/Ha\\\n35\nsbro>\"],[\"Heinz\",\"<http://dbpedia.org/resource/Heinz>\"],[\"San Francisco Chronicle\",\"\\\n36\n<http://dbpedia.org/resource/San_Francisco_Chronicle>\"]]\n37\ncountries:\n[[\"Brazil\",\"<http://dbpedia.org/resource/Brazil>\"],[\"Canada\",\"<http://dbp\\\n38\nedia.org/resource/Canada>\"]]\n39\nEnter text (all on one line)\nNote that entities that are not recognized as Wikipedia objects don’t get recognized.\nBag of Words Classification Model\nThe file Categorize.hs contains a simple bag of words classification model. To prepare the classi-\nfication models, I collected a large set of labelled text. Labels were “chemistry”, “computers”, etc.\nI ranked words based on how often they appeared in training texts for a classification category,\nnormalized by how often they appeared in all training texts. This example uses two auto-generated\nand data-specific Haskell files, one for single words and the other for two adjacent word pairs:\n• Category1Gram.hs\n• Category2Gram.hs\nIn NLP work, single words are sometimes called 1grams and two word adjacent pairs are referred\nto as 2grams. Here is a small amount of data from Category1Gram.hs:\n\nNatural Language Processing Tools\n88\n1\nmodule Category1Gram (**onegrams**) where\n2\n3\nimport qualified Data.Map as M\n4\n5\nchemistry = M.fromList [(\"chemical\", 1.15), (\"atoms\", 6.95),\n6\n(\"reaction\", 6.7), (\"energy\", 6.05),\n7\n... ]\n8\ncomputers = M.fromList [(\"software\", 4.6), (\"network\", 4.65),\n9\n(\"linux\", 3.6), (\"device\", 3.55), (\"computers\", 3.05),\n10\n(\"storage\", 2.7), (\"disk\", 2.3),\n11\n... ]\n12\netc.\nHere is a small amount of data from Category2Gram.hs:\n1\nmodule Category2Gram (**twograms**) where\n2\n3\nimport qualified Data.Map as M\n4\n5\nchemistry = M.fromList [(\"chemical reaction\", 1.55),\n6\n(\"atoms molecules\", 0.6),\n7\n(\"periodic table\", 0.5),\n8\n(\"chemical reactions\", 0.5),\n9\n(\"carbon atom\", 0.5),\n10\n... ]\n11\ncomputers = M.fromList [(\"computer system\", 0.9),\n12\n(\"operating system\", 0.75),\n13\n(\"random memory\", 0.65),\n14\n(\"computer science\", 0.65),\n15\n(\"computer program\", 0.6),\n16\n... ]\n17\netc.\nIt is very common to use term frequencies for single words for classification models. One problem\nwith using single words is that the evidence that any word gives for a classification is independent of\nthe surrounding words in text being evaluated. By also using word pairs (two word combinations are\noften called 2grams or two-grams) we pick up patterns like “not good” giving evidence for negative\nsentiment even with the word “good” in text being evaluated. For my own work, I have a huge\ncorpus of 1gram, 2gram, 3gram, and 4gram data sets. For the purposes of the following example\nprogram, I am only using 1gram and 2gram data.\nThe following listing shows the file Categorize.hs. Before looking at the entire example, let’s focus\nin on some of the functions I have defined for using the word frequency data to categorized text.\n\nNatural Language Processing Tools\n89\n*Categorize> :t stemWordsInString\nstemWordsInString :: String -> [Char]\n*Categorize> stemWordsInString \"Banking industry is sometimes known for fraud.\"\n\"bank industri is sometim known for fraud\"\nstemScoredWordList is used to create a 1gram to word relevance score for each category. The keys\nare word stems.\n*Categorize> stemScoredWordList onegrams\n[(\"chemistri\",fromList [(\"acid\",1.15),(\"acids\",0.8),(\"alcohol\",0.95),(\"atom\",4.45)\nNotice that “chemistri” is the stemmed version of “chemistry”, “bank” for “banks”, etc. stem2 is a\n2gram frequency score by category mapping where the keys are word stems:\n*Categorize> stem2\n[(\"chemistry\",fromList [(\"atom molecul\",0.6),(\"carbon atom\",0.5),(\"carbon carbon\",0.\\\n5),\nstem1 is like stem2, but for stemmed 1grams, not 2grams:\n*Categorize> stem1\n[(\"chemistry\",fromList [(\"acid\",0.8),(\"chang\",1.05),(\"charg\",0.95),(\"chemic\",1.15),(\\\n\"chemistri\",1.45),\nscore is called with a list or words and a word value mapping. Here is an example:\n*Categorize> :t score\nscore\n:: (Enum t, Fractional a, Num t, Ord a, Ord k) =>\n[k] -> [(t1, M.Map k a)] -> [(t, a)]\n*Categorize> score [\"atom\", \"molecule\"] onegrams\n[(0,8.2),(25,2.4)]\nThis output is more than a little opaque. The pair (0, 8.2) means that the input words [“atom”,\n“molecule”] have a score of 8.2 for category indexed at 0 and the pair (25,2.4) means that the input\nwords have a score of 2.4 for the category at index 25. The category at index 0 is chemistry and the\ncategory at index 25 is physics as we can see by using the higher level function bestCategories1\nthat caluculates categories for a word sequence using 1gram word data:\n\nNatural Language Processing Tools\n90\n*Categorize> :t bestCategories1\nbestCategories1 :: [[Char]] -> [([Char], Double)]\n*Categorize> bestCategories1 [\"atom\", \"molecule\"]\n[(\"chemistry\",8.2),(\"physics\",2.4)]\nThe top level function bestCategories uses 1gram data. Here is an example for using it:\n*Categorize> splitWords \"The chemist made a periodic table and explained a chemical \\\nreaction\"\n[\"the\",\"chemist\",\"made\",\"a\",\"periodic\",\"table\",\"and\",\"explained\",\"a\",\"chemical\",\"rea\\\nction\"]\n*Categorize> bestCategories1 $ splitWords \"The chemist made a periodic table and exp\\\nlained a chemical reaction\"\n[(\"chemistry\",11.25),(\"health_nutrition\",1.2)]\nNotice that these words were also classified as category “health_nutrition” but with a low score of\n1.2. The score for “chemistry” is almost an order of magnitude larger. bestCategories sorts return\nvalues in “best first” order.\nsplitWords is used to split a string into word tokens before calling bestCategories.\nHere is the entire example in file Categorize.hs:\n1\nmodule Categorize (bestCategories, splitWords, bigram) where\n2\n3\nimport qualified Data.Map as M\n4\nimport Data.List (sortBy)\n5\n6\nimport Category1Gram (onegrams)\n7\nimport Category2Gram (twograms)\n8\n9\nimport Sentence (segment)\n10\n11\nimport Stemmer (stem)\n12\n13\nimport Utils (splitWords, bigram, bigram_s)\n14\n15\ncatnames1 = map fst onegrams\n16\ncatnames2 = map fst twograms\n17\n18\nstemWordsInString s = init $ concatMap ((++ \" \") . stem) (splitWords s)\n19\n20\nstemScoredWordList = map (\\(str,score) -> (stemWordsInString str, score))\n21\n\nNatural Language Processing Tools\n91\n22\nstem2 = map (\\(category, swl) ->\n23\n(category, M.fromList (stemScoredWordList (M.toList swl))))\n24\ntwograms\n25\n26\nstem1 = map (\\(category, swl) ->\n27\n(category, M.fromList (stemScoredWordList (M.toList swl))))\n28\nonegrams\n29\n30\nscoreCat wrds amap =\n31\nsum $ map (\\x ->\nM.findWithDefault 0.0 x amap) wrds\n32\n33\nscore wrds amap =\n34\nfilter (\\(a, b) -> b > 0.9) $ zip [0..] $ map (\\(s, m) -> scoreCat wrds m) amap\n35\n36\ncmpScore (a1, b1) (a2, b2) = compare b2 b1\n37\n38\nbestCategoriesHelper wrds ngramMap categoryNames=\n39\nlet tg = bigram_s wrds in\n40\nmap (first (categoryNames !!)) $ sortBy cmpScore $ score wrds ngramMap\n41\n42\nbestCategories1 wrds =\n43\ntake 3 $ bestCategoriesHelper wrds onegrams catnames1\n44\n45\nbestCategories2 wrds =\n46\ntake 3 $ bestCategoriesHelper (bigram_s wrds) twograms catnames2\n47\n48\nbestCategories1stem wrds =\n49\ntake 3 $ bestCategoriesHelper wrds stem1 catnames1\n50\n51\nbestCategories2stem wrds =\n52\ntake 3 $ bestCategoriesHelper (bigram_s wrds) stem2 catnames2\n53\n54\nbestCategories :: [String] -> [(String, Double)]\n55\nbestCategories wrds =\n56\nlet sum1 = M.unionWith (+) (M.fromList $ bestCategories1 wrds) ( M.fromList $ best\\\n57\nCategories2 wrds)\n58\nsum2 = M.unionWith (+) (M.fromList $ bestCategories1stem wrds) ( M.fromList $ \\\n59\nbestCategories2stem wrds)\n60\nin sortBy cmpScore $ M.toList $ M.unionWith (+) sum1 sum2\n61\n62\nmain = do\n63\nlet s = \"The sport of hocky is about 100 years old by ahdi dates. American Footb\\\n64\nall is a newer sport. Programming is fun. Congress passed a new budget that might he\\\n\nNatural Language Processing Tools\n92\n65\nlp the economy. The frontier initially was a value path. The ai research of john mcc\\\n66\narthy.\"\n67\nprint $ bestCategories1 (splitWords s)\n68\nprint $ bestCategories1stem (splitWords s)\n69\nprint $ score (splitWords s) onegrams\n70\nprint $ score (bigram_s (splitWords s)) twograms\n71\nprint $ bestCategories2 (splitWords s)\n72\nprint $ bestCategories2stem (splitWords s)\n73\nprint $ bestCategories (splitWords s)\nHere is the output:\n1\n$ stack ghci\n2\n:l Categorize.hs\n3\n*Categorize> main\n4\n[(\"computers_ai\",17.900000000000002),(\"sports\",9.75),(\"computers_ai_search\",6.2)]\n5\n[(\"computers_ai\",18.700000000000003),(\"computers_ai_search\",8.1),(\"computers_ai_lear\\\n6\nning\",5.7)]\n7\n[(2,17.900000000000002),(3,1.75),(4,5.05),(6,6.2),(9,1.1),(10,1.2),(21,2.7),(26,1.1)\\\n8\n,(28,1.6),(32,9.75)]\n9\n[(2,2.55),(6,1.0),(32,2.2)]\n10\n[(\"computers_ai\",2.55),(\"sports\",2.2),(\"computers_ai_search\",1.0)]\n11\n[(\"computers_ai\",1.6)]\n12\n[(\"computers_ai\",40.75000000000001),(\"computers_ai_search\",15.3),(\"sports\",11.95),(\"\\\n13\ncomputers_ai_learning\",5.7)]\nGiven that the variable s contains some test text, line 4 of this output was generated by evaluating\nbestCategories1 (splitWords s), lines 5-6 by evaluating bestCategories1stem (splitWords s), lines\n7-8 from score (splitWords s) onegrams, line 9 from core (bigram_s (splitWords s)) twograms,\nline 10 from bestCategories2 (splitWords s), line 11 from bestCategories2stem (splitWords s),\nand lines 12-13 from bestCategories (splitWords s).\nI called all of the utility fucntions in function main to demonstrate what they do but in practice I\njust call function bestCategories in my applications.\nText Summarization\nThis application uses both the Categorize.hs code and the 1gram data from the last section. The\nalgorithm I devised for this example is based on a simple idea: we categorize text and keep track of\nwhich words provide the strongest evidence for the highest ranked categories. We then return a few\nsentences from the original text that contain the largest numbers of these important words.\n\nNatural Language Processing Tools\n93\nmodule Summarize (summarize, summarizeS) where\nimport qualified Data.Map as M\nimport Data.List.Utils (replace)\nimport Data.Maybe (fromMaybe)\nimport Categorize (bestCategories)\nimport Sentence (segment)\nimport Utils (splitWords, bigram_s, cleanText)\nimport Category1Gram (onegrams)\nimport Category2Gram (twograms)\nscoreSentenceHelper words scoreMap = -- just use 1grams for now\nsum $ map (\\word ->\nM.findWithDefault 0.0 word scoreMap) words\nsafeLookup key alist =\nfromMaybe 0 $ lookup key alist\nscoreSentenceByBestCategories words catDataMaps bestCategories =\nmap (\\(category, aMap) ->\n(category, safeLookup category bestCategories *\nscoreSentenceHelper words aMap)) catDataMaps\nscoreForSentence words catDataMaps bestCategories =\nsum $ map snd $ scoreSentenceByBestCategories words catDataMaps bestCategories\nsummarize s =\nlet words = splitWords $ cleanText s\nbestCats = bestCategories words\nsentences = segment s\nresult1grams = map (\\sentence ->\n(sentence,\nscoreForSentence (splitWords sentence)\nonegrams bestCats))\nsentences\nresult2grams = map (\\sentence ->\n(sentence,\nscoreForSentence (bigram_s (splitWords sentence))\ntwograms bestCats))\nsentences\nmergedResults = M.toList $ M.unionWith (+)\n(M.fromList result1grams) (M.fromList result1grams)\n\nNatural Language Processing Tools\n94\nc400 = filter (\\(sentence, score) -> score > 400) mergedResults\nc300 = filter (\\(sentence, score) -> score > 300) mergedResults\nc200 = filter (\\(sentence, score) -> score > 200) mergedResults\nc100 = filter (\\(sentence, score) -> score > 100) mergedResults\nc000 = mergedResults in\nif not (null c400) then c400 else if not (null c300) then c300 else if not (null c\\\n200) then c200 else if not (null c100) then c100 else c000\nsummarizeS s =\nlet a = replace \"\\\"\" \"'\" $ concatMap (\\x -> fst x ++ \" \") $ summarize s in\nif not (null a) then a else safeFirst $ segment s where\nsafeFirst x\n| length x > 1 = head x ++ x !! 1\n| not (null x)\n= head x\n| otherwise\n= \"\"\nmain = do\nlet s = \"Plunging European stocks, wobbly bonds and grave concerns about the healt\\\nh of Portuguese lender Banco Espirito Santo SA made last week feel like a rerun of t\\\nhe euro crisis, but most investors say it was no more than a blip for a resurgent re\\\ngion. Banco Espirito Santo has been in investorsâ€™ sights since December, when The \\\nWall Street Journal first reported on accounting irregularities at the complex firm.\\\nNerves frayed on Thursday when Banco Espirito Santo's parent company said it wouldn\\\n't be able to meet some short-term debt obligations.\"\nprint $ summarize s\nprint $ summarizeS s\nLazy evaluation allows us in function summarize to define summaries of various numbers of\nsentences, but not all of these possible summaries are calculated.\n$ stack ghci\n*Main ... > :l Summarize.hs\n*Summarize> main\n[(\"Nerves frayed on Thursday when Banco Espirito Santo's parent company said it woul\\\ndn't be able to meet some short-term debt obligations.\",193.54500000000002)]\n\"Nerves frayed on Thursday when Banco Espirito Santo's parent company said it wouldn\\\n't be able to meet some short-term debt obligations. \"\nPart of Speech Tagging\nWe close out this chapter with the Haskell version of my part of speech (POS) tagger that I originally\nwrote in Common Lisp, then converted to Ruby and Java. The file LexiconData.hs is similar to the\n\nNatural Language Processing Tools\n95\nlexical data files seen earlier: I am defining a map where keys a words and map values are POS\ntokens like NNP (proper noun), RB (adverb), etc. The file README.md contains a complete list of\nPOS tag definitions.\nThe example code and data for this section is in the directory FastTag.\nThis listing shows a tiny representative part of the POS definitions in LexiconData.hs:\nlexicon = M.fromList [(\"AARP\", \"NNP\"), (\"Clinic\", \"NNP\"), (\"Closed\", \"VBN\"),\n(\"Robert\", \"NNP\"), (\"West-German\", \"JJ\"),\n(\"afterwards\", \"RB\"), (\"arises\", \"VBZ\"),\n(\"attacked\", \"VBN\"), ...]\nBefore looking at the code example listing, let’s see how the functions defined in fasttag.hs work in\na GHCi repl:\n*Main LexiconData> bigram [\"the\", \"dog\", \"ran\",\n\"around\", \"the\", \"tree\"]\n[[\"the\",\"dog\"],[\"dog\",\"ran\"],[\"ran\",\"around\"],\n[\"around\",\"the\"],[\"the\",\"tree\"]]\n*Main LexiconData> tagHelper \"car\"\n[\"car\",\"NN\"]\n*Main LexiconData> tagHelper \"run\"\n[\"run\",\"VB\"]\n*Main LexiconData> substitute [\"the\", \"dog\", \"ran\", \"around\",\n\"the\", \"tree\"]\n[[[\"the\",\"DT\"],[\"dog\",\"NN\"]],[[\"dog\",\"NN\"],[\"ran\",\"VBD\"]],\n[[\"ran\",\"VBD\"],[\"around\",\"IN\"]],[[\"around\",\"IN\"],[\"the\",\"DT\"]],\n[[\"the\",\"DT\"],[\"tree\",\"NN\"]]]\n*Main LexiconData> fixTags $\nsubstitute [\"the\", \"dog\", \"ran\",\n\"around\", \"the\", \"tree\"]\n[\"NN\",\"VBD\",\"IN\",\"DT\",\"NN\"]\nFunction bigram takes a list or words and returns a list of word pairs. We need the word pairs\nbecause parts of the tagging algorithm needs to see a word with its preceeding word. In an imperative\nlanguage, I would loop over the words and for a word at index i I would have the word at index i -\n1. In a functional language, we avoid using loops and in this case create a list of adjacent word pairs\nto avoid having to use an explicit loop. I like this style of functional programming but if you come\nfrom years of using imperative language like Java and C++ it takes some getting used to.\ntagHelper converts a word into a list of the word and its likely tag. substitute applies tagHelper to\na list of words, getting the most probable tag for each word. The function fixTags will occasionally\noverride the default word tags based on a few rules that are derived from Eric Brill’s paper A Simple\nRule-Based Part of Speech Tagger²⁴.\n²⁴http://aclweb.org/anthology/A92-1021\n\nNatural Language Processing Tools\n96\nHere is the entire example:\n1\nmodule Main where\n2\n3\nimport qualified Data.Map as M\n4\nimport Data.Strings (strEndsWith, strStartsWith)\n5\nimport Data.List (isInfixOf)\n6\n7\nimport LexiconData (lexicon)\n8\n9\nbigram :: [a] -> [[a]]\n10\nbigram [] = []\n11\nbigram [_] = []\n12\nbigram xs = take 2 xs : bigram (tail xs)\n13\n14\ncontainsString word substring = isInfixOf substring word\n15\n16\nfixTags twogramList =\n17\nmap\n18\n-- in the following inner function, [last,current] might be bound,\n19\n-- for example, to [[\"dog\",\"NN\"],[\"ran\",\"VBD\"]]\n20\n(\\[last, current] ->\n21\n-- rule 1: DT, {VBD | VBP} --> DT, NN\n22\nif last !! 1 == \"DT\" && (current !! 1 == \"VBD\" ||\n23\ncurrent !! 1 == \"VB\" ||\n24\ncurrent !! 1 == \"VBP\")\n25\nthen \"NN\"\n26\nelse\n27\n-- rule 2: convert a noun to a number (CD) if \".\" appears in the word\n28\nif (current !! 1) !! 0 == 'N' && containsString (current !! 0) \".\"\n29\nthen \"CD\"\n30\nelse\n31\n-- rule 3: convert a noun to a past participle if\n32\n--\nwords.get(i) ends with \"ed\"\n33\nif (current !! 1) !! 0 == 'N' && strEndsWith (current !! 0) \"ed\"\n34\nthen \"VBN\"\n35\nelse\n36\n-- rule 4: convert any type to adverb if it ends in \"ly\"\n37\nif strEndsWith (current !! 0) \"ly\"\n38\nthen \"RB\"\n39\nelse\n40\n-- rule 5: convert a common noun (NN or NNS) to an\n41\n--\nadjective if it ends with \"al\"\n\nNatural Language Processing Tools\n97\n42\nif strStartsWith (current !! 1) \"NN\" &&\n43\nstrEndsWith (current !! 1) \"al\"\n44\nthen \"JJ\"\n45\nelse\n46\n-- rule 6: convert a noun to a verb if the preceeding\n47\n--\nword is \"would\"\n48\nif strStartsWith (current !! 1) \"NN\" &&\n49\n(last !! 0) == \"would\" -- should be case insensitive\n50\nthen \"VB\"\n51\nelse\n52\n-- rule 7: if a word has been categorized as a\n53\n--\ncommon noun and it ends with \"s\",\n54\n--\nthen set its type to plural common noun (NNS)\n55\nif strStartsWith (current !! 1) \"NN\" &&\n56\nstrEndsWith (current !! 0) \"s\"\n57\nthen \"NNS\"\n58\nelse\n59\n-- rule 8: convert a common noun to a present\n60\n--\nparticiple verb (i.e., a gerand)\n61\nif strStartsWith (current !! 1) \"NN\" &&\n62\nstrEndsWith (current !! 0) \"ing\"\n63\nthen \"VBG\"\n64\nelse (current !! 1))\n65\ntwogramList\n66\n67\nsubstitute tks = bigram $ map tagHelper tks\n68\n69\ntagHelper token =\n70\nlet tags = M.findWithDefault [] token lexicon in\n71\nif tags == [] then [token, \"NN\"] else [token, tags]\n72\n73\ntag tokens = fixTags $ substitute ([\"\"] ++ tokens)\n74\n75\n76\nmain = do\n77\nlet tokens = [\"the\", \"dog\", \"ran\", \"around\", \"the\", \"tree\", \"while\",\n78\n\"the\", \"cat\", \"snaked\", \"around\", \"the\", \"trunk\",\n79\n\"while\", \"banking\", \"to\", \"the\", \"left\"]\n80\nprint $ tag tokens\n81\nprint $ zip tokens $ tag tokens\n\nNatural Language Processing Tools\n98\n*Main LexiconData> main\n[\"DT\",\"NN\",\"VBD\",\"IN\",\"DT\",\"NN\",\"IN\",\"DT\",\"NN\",\"VBD\",\"IN\",\"DT\",\n\"NN\",\"IN\",\"VBG\",\"TO\",\"DT\",\"VBN\"]\n[(\"the\",\"DT\"),(\"dog\",\"NN\"),(\"ran\",\"VBD\"),(\"around\",\"IN\"),\n(\"the\",\"DT\"),(\"tree\",\"NN\"),(\"while\",\"IN\"),(\"the\",\"DT\"),\n(\"cat\",\"NN\"),(\"snaked\",\"VBD\"),(\"around\",\"IN\"),(\"the\",\"DT\"),\n(\"trunk\",\"NN\"),(\"while\",\"IN\"),(\"banking\",\"VBG\"),(\"to\",\"TO\"),\n(\"the\",\"DT\"),(\"left\",\"VBN\")]\nThe README.md file contains definitions of the POS definitions. Here are the ones used in this\nexample:\nDT Determiner\nthe,some\nNN noun\ndog,cat,road\nVBD verb, past tense\nate,ran\nIN Preposition\nof,in,by\nNatural Language Processing Wrap Up\nNLP is a large topic. I have attempted to show you just the few tricks that I use often and are simple\nto implement. I hope that you reuse the code in this chapter in your own projects when you need to\ndetect entities, classify text, summarize text, and assign part of speech tags to words in text.\n\nLinked Data and the Semantic Web\nI am going to show you how to query semantic web data sources on the web and provide examples\nfor how you might use this data in applications. I have written two previous books on the semantic\nweb, one covering Common Lisp and the other covering JVM languages Java, Scala, Clojure, and\nRuby. You can get free PDF versions on the book page of www.markwatson.com²⁵. If you enjoy the\nlight introduction in this chapter then please do download a free copy of my semantic web book for\nmore material on RDF, RDFS, and SPARQL.\nI like to think of the semantic web and linked data resources as:\n• A source of structured data on the web. These resources are called SPARQL endpoints.\n• Data is represented by data triples: subject, predicate, and object. The subject of one triple can\nbe the object of another triple. Predicates are relationships; a few examples: “owns”, “is part\nof”, “author of”, etc.\n• Data that is accessed via the SPARQL query language.\n• A source of data that may or may not be available. SPARQL endpoints are typically available\nfor free use and they are sometimes unavailable. Although not covered here, I sometimes work\naround this problem by adding a caching layer to SPARQL queries (access key being a SPARQL\nquery string, the value being the query results). This caching speeds up development and\nrunning unit tests, and sometimes saves a customer demo when a required SPARQL endpoint\ngoes offline at an inconvenient time.\nDBPedia is the semantic web version of Wikipedia²⁶. The many millions of data triples that make\nup DBPedia are mostly derived from the structured “info boxes” on Wikipedia pages.\nAs you are learning SPARQL use the DBPedia SPARQL endpoint²⁷ to practice. As a practitioner who\nuses linked data, for any new project I start by identifying SPARQL endpoints for possibly useful\ndata. I then interactively experiment with SPARQL queries to extract the data I need. Only when\nI am satisfied with the choice of SPARQL endpoints and SPARQL queries do I write any code to\nautomatically fetch linked data for my application.\nPro tip: I mentioned SPARQL query caching. I sometimes cache query results in a local database,\nsaving the returned RDF data indexed by the SPARQL query. You can also store the cache timestamp\nand refresh the cache every few weeks as needed. In addition to making development and unit testing\nfaster, your applications will be more resilient.\nIn the last chapter “Natural Language Processing Tools” we resolved entities in natural language text\nto DBPedia (semantic web SPAQL endpoint for Wikipedia) URIs. Here we will use some of these\nURIs to demonstrate fetching real world knowledge that you might want to use in applications.\n²⁵http://www.markwatson.com/books/\n²⁶http://wiki.dbpedia.org/\n²⁷http://dbpedia.org/sparql\n\nLinked Data and the Semantic Web\n100\nThe SPARQL Query Language\nExample RDF N3 triples (subject, predicate, object) might look like:\n<http://www.markwatson.com>\n<http://dbpedia.org/ontology/owner>\n\"Mark Watson\" .\nElement of triples can be URIs or string constants. Triples are often written all on one line; I split it\nto three lines to fit the page width. Here the subject is the URI for my web site, the predicate is a\nURI defining an ownership relationship, and the object is a string literal.\nIf you want to see details for any property or other URI you see, then “follow your nose” and open the\nURI in a web browser. For example remove the brackets from the owner property URI http://dbpedia.\norg/ontology/owner²⁸ and open it in a web browser. For working with RDF data programatically, it\nis convenient using full URI. For humans reading RDF, the N3 notation is better because it supports\ndefining URI standard prefixes for use as abbreviations; for example:\nprefix ontology: <http://dbpedia.org/ontology/>\n<http://www.markwatson.com>\nontology:owner\n\"Mark Watson\" .\nIf you wanted to find all things that I own (assuming this data was in a public RDF repository, which\nit isn’t) then we might think to match the pattern:\nprefix ontology: <http://dbpedia.org/ontology/>\n?subject ontology:owner \"Mark Watson\"\nAnd return all URIs matching the variable ?subject as the query result. This is the basic idea of\nmaking SPARQL queries.\nThe following SPARQL query will be implemented later in Haskell using the HSparql library:\n²⁸http://dbpedia.org/ontology/owner\n\nLinked Data and the Semantic Web\n101\n1\nprefix resource: <http://dbpedia.org/resource/>\n2\nprefix dbpprop: <http://dbpedia.org/property/>\n3\nprefix foaf: <http://xmlns.com/foaf/0.1/>\n4\n5\nSELECT *\n6\nWHERE {\n7\n?s dbpprop:genre resource:Web_browser .\n8\n?s foaf:name ?name .\n9\n} LIMIT 5\nIn this last SPARQL query example, the triple patterns we are trying to match are inside a WHERE\nclause. Notice that in the two triple patterns, the subject field of each is the variable ?s. The first\npattern matches all DBPedia triples with a predicate http://dbpedia.org/property/genre and an object\nequal to http://dbpedia.org/resource/Web_browser. We then find all triples with the same subject\nbut with a predicate equal to http://xmlns.com/foaf/0.1/name.\nEach result from this query will contain two values for variables ?s and ?name: a DBPedia URI for\nsome thing and the name for that thing. Later we will run this query using Haskell code and you\ncan see what the output might look like.\nSometimes when I am using a specific SPARQL query in an application, I don’t bother defining\nprefixes and just use URIs in the query. As an example, suppose I want to return the Wikipedia (or\nDBPedia) abstract for IBM. I might use a query such as:\n1\nselect * where {\n2\n<http://dbpedia.org/resource/IBM>\n3\n<http://dbpedia.org/ontology/abstract>\n4\n?o .\n5\nFILTER langMatches(lang(?o), \"EN\")\n6\n} LIMIT 100\nIf you try this query using the web interface for DBPedia SPARQL queries²⁹ you get just one result\nbecause of the FILTER option that only returns English language results. You could also use FR for\nFrench results, GE for German results, etc.\nA Haskell HTTP Based SPARQL Client\nOne approach to query the DBPedia SPARQL endpoint is to build a HTTP GET request, send it to\nthe SPARQL endpoint server, and parse the returned XML response. We will start with this simple\napproach. You will recognize the SPARQL query from the last section:\n²⁹http://dbpedia.org/sparql/\n\nLinked Data and the Semantic Web\n102\n1\n{-# LANGUAGE OverloadedStrings #-}\n2\n3\nmodule HttpSparqlClient where\n4\n5\nimport Network.HTTP.Conduit (simpleHttp)\n6\nimport Network.HTTP.Base (urlEncode)\n7\nimport Text.XML.HXT.Core\n8\nimport Text.HandsomeSoup\n9\nimport qualified Data.ByteString.Lazy.Char8 as B\n10\n11\nbuildQuery :: String -> [Char]\n12\nbuildQuery sparqlString =\n13\n\"http://dbpedia.org/sparql/?query=\" ++ urlEncode sparqlString\n14\n15\nmain :: IO ()\n16\nmain = do\n17\nlet query = buildQuery \"select * where {<http://dbpedia.org/resource/IBM> <http://\\\n18\ndbpedia.org/ontology/abstract> ?o . FILTER langMatches(lang(?o), \\\"EN\\\")} LIMIT 100\"\n19\nres <- simpleHttp query\n20\nlet doc = readString []\n(B.unpack res)\n21\nputStrLn \"\\nAbstracts:\\n\"\n22\nabstracts <- runX $ doc >>> css \"binding\" >>>\n23\n(getAttrValue \"name\" &&& (deep getText))\n24\nprint abstracts\nThe function buildQuery defined in lined 11-13 takes any SPARQL query, URL encodes it so it can\nbe passed as part of a URI, and builds a query string for the DBPedia SPARQL endpoint. The returned\ndata is in XML format. In lines 23-24 I am using the XHT parsing library to extract the names (values\nbound to the variable ?o in the query in line 17). I covered the use of the HandsomeSoup parsing\nlibrary in the chapter Web Scraping.\nWe use runX to execute a series of operations on an XML document (the doc variable). We first\nselect all elements in doc that have the CSS class binding using the css function. Next we extract\nthe value of the name attribute from each selected element using getAttrValue and also extract\nthe text inside the element using the function deep. The &&& operator is used to combine the two\nvalues for the name attribute and the element text into a tuple.\nIn the main function, we use the utility function simpleHttp in line 20 to fetch the results as a\nByteString and in line 21 we unpack this to a regular Haskell String.\n\nLinked Data and the Semantic Web\n103\n1\nPrelude> :l HttpSparqlClient.hs\n2\n[1 of 1] Compiling HttpSparqlClient ( HttpSparqlClient.hs, interpreted )\n3\nOk, modules loaded: HttpSparqlClient.\n4\n*HttpSparqlClient> main\n5\n6\nAbstracts:\n7\n8\n[(\"o\",\"International Business Machines Corporation (commonly referred to as IBM) is \\\n9\nan American multinational technology and consulting corporation, with corporate head\\\n10\nquarters in Armonk, New York.\n11\n...)]\nQuerying Remote SPARQL Endpoints\nWe will write some code in this section to make the example query to get the names of web browsers\nfrom DBPedia. In the last section we made a SPARQL query using fairly low level Haskell libraries.\nWe will be using the high level library HSparql to build SPARQL queries and call the DBPedia\nSPARQL endpoint.\nThe example in this section can be found in SparqlClient/TestSparqlClient.hs. In the main function\nnotice how I have commented out printouts of the raw query results. Because Haskell is type safe,\nextracting the values wrapped in query results requires knowing RDF element return types. I will\nexplain this matching after the program listing:\n1\n-- simple experiments with the excellent HSparql library\n2\n3\nmodule Main where\n4\n5\nimport Database.HSparql.Connection (BindingValue(Bound))\n6\n7\nimport Data.RDF hiding (triple)\n8\nimport Database.HSparql.QueryGenerator\n9\nimport Database.HSparql.Connection (selectQuery)\n10\n11\nwebBrowserSelect :: Query SelectQuery\n12\nwebBrowserSelect = do\n13\nresource <- prefix \"dbprop\" (iriRef \"http://dbpedia.org/resource/\")\n14\ndbpprop\n<- prefix \"dbpedia\" (iriRef \"http://dbpedia.org/property/\")\n15\nfoaf\n<- prefix \"foaf\" (iriRef \"http://xmlns.com/foaf/0.1/\")\n16\nx\n<- var\n17\nname <- var\n18\ntriple x (dbpprop .:. \"genre\") (resource .:. \"Web_browser\")\n\nLinked Data and the Semantic Web\n104\n19\ntriple x (foaf .:. \"name\") name\n20\n21\nreturn SelectQuery { queryVars = [name] }\n22\n23\ncompanyAbstractSelect :: Query SelectQuery\n24\ncompanyAbstractSelect = do\n25\nresource <- prefix \"dbprop\" (iriRef \"http://dbpedia.org/resource/\")\n26\nontology <- prefix \"ontology\" (iriRef \"http://dbpedia.org/ontology/\")\n27\no <- var\n28\ntriple (resource .:. \"Edinburgh_University_Press\") (ontology .:. \"abstract\") o\n29\nreturn SelectQuery { queryVars = [o] }\n30\n31\ncompanyTypeSelect :: Query SelectQuery\n32\ncompanyTypeSelect = do\n33\nresource <- prefix \"dbprop\" (iriRef \"http://dbpedia.org/resource/\")\n34\nontology <- prefix \"ontology\" (iriRef \"http://dbpedia.org/ontology/\")\n35\no <- var\n36\ntriple (resource .:. \"Edinburgh_University_Press\") (ontology .:. \"type\") o\n37\nreturn SelectQuery { queryVars = [o] }\n38\n39\nmain :: IO ()\n40\nmain = do\n41\nsq1 <- selectQuery \"http://dbpedia.org/sparql\" companyAbstractSelect\n42\n--putStrLn \"\\nRaw results of company abstract SPARQL query:\\n\"\n43\n--print sq1\n44\nputStrLn \"\\nWeb browser names extracted from the company abstract query results:\\n\"\n45\ncase sq1 of\n46\nJust a -> print $ map (\\[Bound (LNode (PlainLL s _))] -> s) a\n47\nNothing -> putStrLn \"nothing\"\n48\nsq2 <- selectQuery \"http://dbpedia.org/sparql\" companyTypeSelect\n49\n--putStrLn \"\\nRaw results of company type SPARQL query:\\n\"\n50\n--print sq2\n51\nputStrLn \"\\nWeb browser names extracted from the company type query results:\\n\"\n52\ncase sq2 of\n53\nJust a -> print $ map (\\[Bound (UNode\ns)] -> s) a\n54\nNothing -> putStrLn \"nothing\"\n55\nsq3 <- selectQuery \"http://dbpedia.org/sparql\" webBrowserSelect\n56\n--putStrLn \"\\nRaw results of SPARQL query:\\n\"\n57\n--print sq3\n58\nputStrLn \"\\nWeb browser names extracted from the query results:\\n\"\n59\ncase sq3 of\n60\nJust a -> print $ map (\\[Bound (LNode (PlainLL s _))] -> s) a\n61\nNothing -> putStrLn \"nothing\"\n\nLinked Data and the Semantic Web\n105\nNotes on matching result types of query results:\nYou will notice how I have commented out print statements in the last example. When trying new\nqueries you need to print out the results in order to know how to extract the wrapped query results.\nLet’s look at a few examples:\nIf we print the value for sq1:\nRaw results of company abstract SPARQL query:\nJust [[Bound (LNode (PlainLL \"Edinburgh University Press ...\nwe see that inside a Just we have a list of lists. Each inner list is a Bound wrapping types defined\nin HSparql. We would unwrap sq1 using:\n1\ncase sq1 of\n2\nJust a -> print $ map (\\[Bound (LNode (PlainLL s _))] -> s) a\n3\nNothing -> putStrLn \"nothing\"\nIn a similar way I printed out the values of sq2 and sq3 to see the form os case statement I would\nneed to unwrap them.\nThe output from this example with three queries to the DBPedia SPARQL endpoint is:\n1\nWeb browser names extracted from the company abstract query results in sq1:\n2\n3\n[\"Edinburgh University Press \\195\\168 una casa editrice scientifica di libri accadem\\\n4\nici e riviste, con sede a Edimburgo, in Scozia.\",\"Edinburgh University Press \\195\\16\\\n5\n9 uma editora universit\\195\\161ria com base em Edinburgh, Esc\\195\\179cia.\",\"Edinburg\\\n6\nh University Press is a scholarly publisher of academic books and journals, based in\\\n7\nEdinburgh, Scotland.\"]\n8\n9\nThe type of company is extracted from the company type query results in sq2:\n10\n11\n[\"http://dbpedia.org/resource/Publishing\"]\n12\n13\nWeb browser names extracted from the query results in sq3:\n14\n15\n[\"Grail\",\"ViolaWWW\",\"Kirix Strata\",\"SharkWire Online\",\"MacWeb\",\"Camino\",\"eww\",\"TenFo\\\n16\nurFox\",\"WiseStamp\",\"X-Smiles\",\"Netscape Navigator 2\",\"SimpleTest\",\"AWeb\",\"IBrowse\",\"\\\n17\niCab\",\"ANT Fresco\",\"Netscape Navigator 9.0\",\"HtmlUnit\",\"ZAC Browser\",\"ELinks\",\"ANT G\\\n18\nalio\",\"Nintendo DSi Browser\",\"Nintendo DS Browser\",\"Netscape Navigator\",\"NetPositive\\\n19\n\",\"OmniWeb\",\"Abaco\",\"Flock\",\"Steel\",\"Kazehakase\",\"GNU IceCat\",\"FreeWRL\",\"UltraBrowse\\\n20\nr\",\"AMosaic\",\"NetCaptor\",\"NetSurf\",\"Netscape Browser\",\"SlipKnot\",\"ColorZilla\",\"Inter\\\n\nLinked Data and the Semantic Web\n106\n21\nnet Channel\",\"Obigo Browser\",\"Swiftfox\",\"BumperCar\",\"Swiftweasel\",\"Swiftdove\",\"IEs4L\\\n22\ninux\",\"MacWWW\",\"IBM Lotus Symphony\",\"SlimBrowser\",\"cURL\",\"FoxyTunes\",\"Iceweasel\",\"Me\\\n23\nnuBox\",\"Timberwolf web browser\",\"Classilla\",\"Rockmelt\",\"Galeon\",\"Links\",\"Netscape Na\\\n24\nvigator\",\"NCSA Mosaic\",\"MidasWWW\",\"w3m\",\"PointerWare\",\"Pogo Browser\",\"Oregano\",\"Avan\\\n25\nt Browser\",\"Wget\",\"NeoPlanet\",\"Voyager\",\"Amaya\",\"Midori\",\"Sleipnir\",\"Tor\",\"AOL Explo\\\n26\nrer\"]\nLinked Data and Semantic Web Wrap Up\nIf you enjoyed the material on linked data and DBPedia then please do get a free copy of one of my\nsemantic web books on my website book page³⁰ as well as other SPARQL and linked data tutorials\non the web.\nStructured and sematically labelled data, when it is available, is much easier to process and use\neffectively than raw text and HTML collected from web sites.\n³⁰http://www.markwatson.com/books/\n\nWeb Scraping\nIn my past work I usually used the Ruby scripting language for web scraping but as I use the Haskell\nlanguage more often for projects both large and small I am now using Haskell for web scraping, data\ncollection, and data cleaning tasks. If you worked through the tutorial chapter on impure Haskell\nprogramming then you already know most of what you need to understand this chapter. Here we\nwill walk through a few short examples for common web scraping tasks.\nBefore we start a tutorial about web scraping I want to point out that much of the information on the\nweb is copyright and the first thing that you should do is to read the terms of service for web sites to\ninsure that your use of web scraped data conforms with the wishes of the persons or organizations\nwho own the content and pay to run scraped web sites.\nAs we saw in the last chapter on linked data there is a huge amount of structured data available on the\nweb via web services, semantic web/linked data markup, and APIs. That said, you will frequently\nfind text (usually HTML) that is useful on web sites. However, this text is often at least partially\nunstructured and in a messy and frequently changing format because web pages are meant for\nhuman consumption and making them easy to parse and use by software agents is not a priority of\nweb site owners.\nNote: It takes a while to fetch all of the libraries in the directory WebScraping so please do a stack\nbuild now to get these examples ready to experiment with while you read this chapter.\nUsing the Wreq Library\nThe Wreq library³¹ is an easy way to fetch data from the web. The example in this section fetches\nDBPedia (i.e., the semantic web version of Wikipedia) data in JSON and RDF N3 formats, and also\nfetches the index page from my web site. I will introduce you to the Lens library for extracting data\nfrom data structures, and we will also use Lens in a later chapter when writing a program to play\nBackjack.\nWe will be using function get in the Network.Wreq module that has a type signature:\nget::String -> IO (Response Data.ByteString.Lazy.Internal.ByteString)\nWe will be using the OverloadedStrings language extension to facilitate using both [Char] strings\nand ByteString data types. Note: In the GHCi repl you can use :set -XOverloadedStrings.\nWe use function get to return JSON data; here is a bit of the JSON data returned from calling get\nusing the URI for my web site:\n³¹http://www.serpentine.com/wreq/tutorial.html\n\nWeb Scraping\n108\nResponse {responseStatus = Status {statusCode = 200, statusMessage = \"OK\"},\nresponseVersion = HTTP/1.1,\nresponseHeaders =\n[(\"Date\",\"Sat, 15 Oct 2016 16:00:59 GMT\"),\n(\"Content-Type\",\"text/html\"),\n(\"Transfer-Encoding\",\"chunked\"),\n(\"Connection\",\"keep-alive\")],\nresponseBody = \"<!DOCTYPE html>\\r\\n<html>\\r\\n<head><title>Mark Watson: con\\\nsultant specializing in artificial intelligence, natural language processing, and ma\\\nchine\\r\\n\nlearning</title>\\r\\n\n<meta name=\\\"viewport\\\" content=\\\"width=device-\\\nwidth, initial-scale=1.0\\\">\\r\\n\n<meta name=\\\"msvalidate.01\\\" content=\\\"D980F894E9\\\n4AA6335FB595676DFDD5E6\\\"/>\\r\\n\n<link href=\\\"/css/bootstrap.min.css\\\" rel=\\\"styles\\\nheet\\\" type=\\\"text/css\\\">\\r\\n\n<link href=\\\"/css/bootstrap-theme.min.css\\\" rel=\\\"s\\\ntylesheet\\\" type=\\\"text/css\\\">\\r\\n\n<link href=\\\"/css/mark.css\\\" rel=\\\"stylesheet\\\\\n\" type=\\\"text/css\\\">\\r\\n\n<link rel=\\\"manifest\\\" href=\\\"/manifest.json\\\">\\r\\n\n<\\\nstyle type=\\\"text/css\\\">\nbody {\\r\\n\npadding-top: 60px;\\r\\n\n}</style>\\r\\n\\r\\n\n<link rel\\\n=\\\"canonical\\\" href=https://www.markwatson.com/ />\\r\\n</head>\\r\\n<body\nhref=\\\"http:\\\n//blog.markwatson.com\\\">Blog</a></li>\\r\\n\n<li class=\\\"\\\"><a href=\\\"/books/\\\">My Books</a>\nAs an example, the Lens expression for extracting the response status code is (r is the IO Response\ndata returned from calling get):\n(r ^. responseStatus . statusCode)\nresponseStatus digs into the top level response structure and statusCode digs further in to fetch\nthe code 200. To get the actual contents of the web page we can use the responseBody function:\n(r ^. responseBody)\nHere is the code for the entire example:\n\nWeb Scraping\n109\n1\n{-# LANGUAGE OverloadedStrings #-}\n2\n3\n-- reference: http://www.serpentine.com/wreq/tutorial.html\n4\n5\nmodule HttpClientExample where\n6\n7\nimport Network.Wreq\n8\nimport Control.Lens\n-- for ^. ^?\n9\nimport Data.Maybe (fromJust)\n10\n11\nfetchURI uri = do\n12\nputStrLn $ \"\\n\\n***\nFetching \" ++ uri\n13\nr <- get uri\n14\nputStrLn $ \"status code: \" ++ (show (r ^. responseStatus . statusCode))\n15\nputStrLn $ \"content type: \" ++ (show (r ^? responseHeader \"Content-Type\"))\n16\nputStrLn $ \"respose body: \" ++ show (fromJust (r ^? responseBody))\n17\n18\nmain :: IO ()\n19\nmain = do\n20\n-- JSON from DBPedia\n21\nfetchURI \"http://dbpedia.org/data/Sedona_Arizona.json\"\n22\n-- N3 RDF from DBPedia\n23\nfetchURI \"http://dbpedia.org/data/Sedona_Arizona.n3\"\n24\n-- my web site\n25\nfetchURI \"http://markwatson.com\"\nThis example produces a lot of printout, so I a just showing a small bit here (the text from the body\nis not shown):\n1\n*Main> :l HttpClientExample\n2\n[1 of 1] Compiling HttpClientExample ( HttpClientExample.hs, interpreted )\n3\nOk, modules loaded: HttpClientExample.\n4\n*HttpClientExample> main\n5\n6\n***\nFetching http://dbpedia.org/data/Sedona_Arizona.json\n7\nstatus code: 200\n8\ncontent type: Just \"application/json\"\n9\nrespose body: \"{\\n\n\\\"http://en.wikipedia.org/wiki/Sedona_Arizona\\\" : { \\\"http://xml\\\n10\nns.com/foaf/0.1/primaryTopic\\\" : [ { \\\"type\\\" : \\\"uri\\\", \\\"value\\\" : \\\"http://dbpedi\\\n11\na.org/resource/Sedona_Arizona\\\" } ] } ,\\n\n\\\"http://dbpedia.org/resource/Sedona_Ariz\\\n12\nona\\\" : { \\\"http://www.w3.org/2002/07/owl#sameAs\\\" : [ { \\\"type\\\" : \\\"uri\\\", \\\"value\\\n13\n\\\" : \\\"http://dbpedia.org/resource/Sedona_Arizona\\\" } ] ,\\n\n\\\"http://www.w3.org/2\\\n14\n000/01/rdf-schema#label\\\" : [ { \\\"type\\\" : \\\"literal\\\", \\\"value\\\" : \\\"Sedona Arizona\\\n\nWeb Scraping\n110\n15\n\\\" , \\\"lang\\\" : \\\"en\\\" } ] ,\\n\n\\\"http://xmlns.com/foaf/0.1/isPrimaryTopicOf\\\" : [\\\n16\n{ \\\"type\\\" : \\\"uri\\\", \\\"value\\\" : \\\"http://en.wikipedia.org/wiki/Sedona_Arizona\\\" }\\\n17\n] ,\\n\n\\\"http://www.w3.org/ns/prov#wasDerivedFrom\\\" : [ { \\\"type\\\" : \\\"uri\\\", \\\"v\\\n18\nalue\\\" : \\\"http://en.wikipedia.org/wiki/Sedona_Arizona?oldid=345939723\\\" } ] ,\\n\n\\\n19\n\\\"http://dbpedia.org/ontology/wikiPageID\\\" : [ { \\\"type\\\" : \\\"literal\\\", \\\"value\\\" :\\\n20\n11034313 , \\\"datatype\\\" : \\\"http://www.w3.org/2001/XMLSchema#integer\\\" } ] ,\\n\n\\\\\n21\n\"http://dbpedia.org/ontology/wikiPageRevisionID\\\" : [ { \\\"type\\\" : \\\"literal\\\", \\\"va\\\n22\nlue\\\" : 345939723 , \\\"datatype\\\" : \\\"http://www.w3.org/2001/XMLSchema#integer\\\" } ] \\\n23\n,\\n\n\\\"http://dbpedia.org/ontology/wikiPageRedirects\\\" : [ { \\\"type\\\" : \\\"uri\\\", \\\\\n24\n\"value\\\" : \\\"http://dbpedia.org/resource/Sedona,_Arizona\\\" } ] }\\n}\\n\"\n25\n26\n***\nFetching http://dbpedia.org/data/Sedona_Arizona.n3\n27\nstatus code: 200\n28\ncontent type: Just \"text/n3; charset=UTF-8\"\n29\nrespose body: \"@prefix foaf:\\t<http://xmlns.com/foaf/0.1/> .\\n@prefix wikipedia-en:\\\\\n30\nt<http://en.wikipedia.org/wiki/> .\\n@prefix dbr:\\t<http://dbpedia.org/resource/> .\\n\\\n31\nwikipedia-en:Sedona_Arizona\\tfoaf:primaryTopic\\tdbr:Sedona_Arizona .\\n@prefix owl:\\t\\\n32\n<http://www.w3.org/2002/07/owl#> .\\ndbr:Sedona_Arizona\\towl:sameAs\\tdbr:Sedona_Arizo\\\n33\nna .\\n@prefix rdfs:\\t<http://www.w3.org/2000/01/rdf-schema#> .\\ndbr:Sedona_Arizona\\t\\\n34\nrdfs:label\\t\\\"Sedona Arizona\\\"@en ;\\n\\tfoaf:isPrimaryTopicOf\\twikipedia-en:Sedona_Ar\\\n35\nizona .\\n@prefix prov:\\t<http://www.w3.org/ns/prov#> .\\ndbr:Sedona_Arizona\\tprov:was\\\n36\nDerivedFrom\\t<http://en.wikipedia.org/wiki/Sedona_Arizona?oldid=345939723> .\\n@prefi\\\n37\nx dbo:\\t<http://dbpedia.org/ontology/> .\\ndbr:Sedona_Arizona\\tdbo:wikiPageID\\t110343\\\n38\n13 ;\\n\\tdbo:wikiPageRevisionID\\t345939723 ;\\n\\tdbo:wikiPageRedirects\\t<http://dbpedi\\\n39\na.org/resource/Sedona,_Arizona> .\"\n40\n41\n***\nFetching http://markwatson.com\n42\nstatus code: 200\n43\ncontent type: Just \"text/html\"\n44\nrespose body: \"<!DOCTYPE html>\\r\\n<html>\\r\\n<head><title>Mark Watson: consultant spe\\\n45\ncializing in\n...\nYou might want to experiment in the GHCi repl with the get function and Lens. If so, this will get\nyou started:\n\nWeb Scraping\n111\n*Main> :set -XOverloadedStrings\n*Main> r <- get \"http://dbpedia.org/data/Sedona_Arizona.json\"\n*Main> :t r\nr :: Response ByteString\n*Main> (r ^. responseStatus . statusCode)\n200\n*Main> (r ^? responseHeader \"Content-Type\")\nJust \"application/json\"\n*Main> fromJust (r ^? responseHeader \"Content-Type\")\n\"application/json\"\n*Main> (fromJust (r ^? responseBody))\n\"{\\n\n\\\"http://en.wikipedia.org/wiki/Sedona_Arizona\\\" : { ... not shown ... \\\"\nIn the following section we will use the HandsomeSoup library for parsing HTML.\nUsing the HandsomeSoup Library for Parsing HTML\nWe will now use the Handsome Soup³² library to parse HTML. Handsome Soup allows us to use CSS\nstyle selectors to extract specific elements from the HTML from a web page. The HXT lower level\nlibrary provides modeling HTML (and XML) as a tree structure and an Arrow³³ style interface for\ntraversing the tree structures and extract data. Arrows are a generalization of monads to manage\ncalculations given a context. I will touch upon just enough material on Arrows for you to understand\nthe examples in this chapter. Handsome Soup also provides a high level utility function fromUrl to\nfetch web pages; the type of fromUrl is:\nfromUrl\n:: String -> IOSArrow b (Data.Tree.NTree.TypeDefs.NTree XNode)\nWe will not work directly with the tree structure of the returned data, we will simply use the accessor\nfunctions to extract the data we need. Before looking at the example code listing, let’s look at this\nextraction process (doc is the tree structured data returned from calling fromUrl):\nlinks <- runX $ doc >>> css \"a\" ! \"href\"\nThe runX function runs arrow computations for us. doc is a tree data structure, css allows us to\npattern match on specific HTML elements.\nHere we are using CSS style selection for all “a” anchor HTML elements and digging into the element\nto return the element attribute “href” value for each “a” anchor element. In a similar way, we can\nselect all “img” image elements and dig down into the matched elements to fetch the “src” attributes:\n³²https://github.com/egonSchiele/HandsomeSoup\n³³https://wiki.haskell.org/Arrow\n\nWeb Scraping\n112\nimageSrc <- runX $ doc >>> css \"img\" ! \"src\"\nWe can get the full body text:\nallBodyText <- runX $ doc >>> css \"body\" //> getText\nThe operator //> applied to the function getText will get all text in all nested elements inside the\nbody element. If we had used the operator /> then we would only have fetched the text at the top\nlevel of the body element.\nHere is the full example source listing:\n1\n{-# LANGUAGE OverloadedStrings #-}\n2\n3\n-- references: https://github.com/egonSchiele/HandsomeSoup\n4\n--\nhttp://adit.io/posts/2012-04-14-working_with_HTML_in_haskell.html\n5\n6\nmodule Main where\n7\n8\nimport Text.XML.HXT.Core\n9\nimport Text.HandsomeSoup\n10\n11\n12\nmain :: IO ()\n13\nmain = do\n14\nlet doc = fromUrl \"http://markwatson.com/\"\n15\nputStrLn \"\\n\\n ** LINKS:\\n\"\n16\nlinks <- runX $ doc >>> css \"a\" ! \"href\"\n17\nmapM_ putStrLn links\n18\nh2 <- runX $ doc >>> css \"h2\" ! \"href\"\n19\nputStrLn \"\\n\\n ** ALL H2 ELEMENTS::\\n\"\n20\nmapM_ putStrLn h2\n21\nimageSrc <- runX $ doc >>> css \"img\" ! \"src\"\n22\nputStrLn \"\\n\\n ** ALL IMG ELEMENTS:\\n\"\n23\nmapM_ putStrLn imageSrc\n24\nallBodyText <- runX $ doc >>> css \"body\" //> getText\n25\nputStrLn \"\\n\\n ** TEXT FROM BODY ELEMENT:\\n\"\n26\nmapM_ putStrLn allBodyText\n27\npText <- runX $ doc >>> css \"p\" //> getText -- //> gets all contained text\n28\n-- />\ngets only directly\n29\n--\ncontained text\n30\nputStrLn \"\\n\\n ** ALL P ELEMENTS:\\n\"\n31\nmapM_ putStrLn pText\nThis example prints out several hundred lines; here is the first bit of output:\n\nWeb Scraping\n113\n*Main> :l HandsomeSoupTest.hs\n[1 of 1] Compiling HandsomeSoupTest ( HandsomeSoupTest.hs, interpreted )\nOk, modules loaded: HandsomeSoupTest.\n*HandsomeSoupTest> main\n** LINKS:\n/\n/consulting/\nhttp://blog.markwatson.com\n/books/\n/opensource/\n/fun/\nhttps://github.com/mark-watson\nhttps://plus.google.com/117612439870300277560\nhttps://twitter.com/mark_l_watson\nhttps://www.wikidata.org/wiki/Q18670263\nhttp://markwatson.com/index.rdf\nhttp://markwatson.com/index.ttl\n** ALL IMG ELEMENTS:\n/pictures/Markws.jpg\n** TEXT FROM BODY ELEMENT:\n...\nI find HandsomeSoup to be very convenient for picking apart HTML data fetched from web pages.\nWriting a good spider for any given web site is a process of understanding how the HTML for the\nweb site is structured and what information you need to collect. I strongly suggest that you work\nwith the web page to be spider open in a web browser with “show source code” in another browser\ntab. Then open an interactive GHCi repl and experiment using the HandsomeSoup APIs to get the\ndata you need.\nWeb Scraping Wrap Up\nThere are many Haskell library options for web scraping and cleaning data. In this chapter I showed\nyou just what I use in my projects.\nThe material in this chapter and the chapters on text processing and linked data should be sufficient\nto get you started using online data sources in your applications.\n\nUsing Relational Databases\nWe will see how to use popular libraries for accessing the sqlite and Postgres (sometimes also called\nPostgeSQL) databases in this chapter. I assume that you are already familiar with SQL.\nDatabase Access for Sqlite\nWe will use the sqlite-simple³⁴ library in this section to access Sqlite databases and use the similar\nlibrary postgresql-simple³⁵ in the next section for use with Postgres.\nThere are other good libraries for database connectivity like Persistent³⁶ but I like sqlite-simple and\nit has a gentle learning curve so that is what we will use here. You will learn the basics of database\nconnectivity in this and the next section. Setting up and using sqlite is easy because the sqlite-\nsimple library includes the compiled code for sqlite so configuration requires only the file path to\nthe database file.\n1\n{-# LANGUAGE OverloadedStrings #-}\n2\n3\nmodule Main where\n4\n5\nimport Database.SQLite.Simple\n6\n7\n{-\n8\nCreate sqlite database:\n9\nsqlite3 test.db \"create table test (id integer primary key, str text);\"\n10\n11\nThis example is derived from the example at github.com/nurpax/sqlite-simple\n12\n-}\n13\n14\nmain :: IO ()\n15\nmain = do\n16\nconn <- open \"test.db\"\n17\n-- start by getting table names in database:\n18\ndo\n19\nr <- query_ conn\n20\n\"SELECT name FROM sqlite_master WHERE type='table'\" :: IO [Only String]\n³⁴https://hackage.haskell.org/package/sqlite-simple\n³⁵https://hackage.haskell.org/package/postgresql-simple\n³⁶https://www.stackage.org/package/persistent\n\nUsing Relational Databases\n115\n21\nprint \"Table names in database test.db:\"\n22\nmapM_ (print . fromOnly) r\n23\n24\n-- get the metadata for table test in test.db:\n25\ndo\n26\nr <- query_ conn\n27\n\"SELECT sql FROM sqlite_master WHERE type='table' and name='test'\" ::\n28\nIO [Only String]\n29\nprint \"SQL to create table 'test' in database test.db:\"\n30\nmapM_ (print . fromOnly) r\n31\n32\n-- add a row to table 'test' and then print out the rows in table 'test':\n33\ndo\n34\nexecute conn \"INSERT INTO test (str) VALUES (?)\"\n35\n(Only (\"test string 2\" :: String))\n36\nr2 <- query_ conn \"SELECT * from test\" :: IO [(Int, String)]\n37\nprint \"number of rows in table 'test':\"\n38\nprint (length r2)\n39\nprint \"rows in table 'test':\"\n40\nmapM_ print\nr2\n41\n42\nclose conn\nThe type Only used in line 20 acts as a container for a single value and is defined in the simple-sqlite\nlibrary. It can also be used to pass values for queries like:\nr <- query_ conn \"SELECT name FROM customers where id = ?\" (Only 4::Int)\nTo run this example start by creating a sqlite database that is stored in the file test.db:\nsqlite3 test.db \"create table test (id integer primary key, str text);\"\nThen build and run the example:\nstack build --exec TestSqLite1\nDatabase Access for Postgres\nSetting up and using a database in the last section was easy because the sqlite-simple library includes\nthe compiled code for sqlite so configuration only requires the file path the the database file. The\n\nUsing Relational Databases\n116\nHaskel examples for Postgres will be similar to those for Sqlite. There is some complication in setting\nup Postgres if you do not already have it installed and configured.\nIn any case, you will need to have Postgres installed and set up with a user account for yourself.\nWhen I am installing and configuring Postgres on my Linux laptop, I create a database role markw.\nYou will certainly create a different role/account name so subsitute your role name for markw in\nthe following code examples.\nIf you are using Ubuntu you can install Postgres and create a role using:\nsudo apt-get update\nsudo apt-get install postgresql postgresql-contrib postgresql-server-dev-9.5\nsudo -u postgres createuser --interactive\nEnter name of role to add: markw\nShall the new role be a superuser? (y/n) y\nWe will need to install postgresql-server-dev-9.5 in order to use the Haskell Postgres bindings. Note\nthat your version of Ubuntu Linux may have a different version of the server dev package which\nyou can find using:\naptitude search postgresql-dev\nIf you are using Mac OS X you can then install Postgres as an application which is convenient for\ndevelopment. A role is automatically created with the same name as your OS X “short name.” You\ncan use the “Open psql” button on the interface to open a command line shell that functions like the\npsql command on Ubuntu (or other Linux distributions).\nWe will need to install postgresql-server-dev-9.5 in order to use the Haskell Postgres bindings. Note\nthat your version of Ubuntu Linux may have a different version of the server dev package which\nyou can find using:\naptitude search postgresql-dev\nYou will then want to create a database named haskell and set the password for role/account markw\nto test1 for running the example in this section:\n\nUsing Relational Databases\n117\ncreatedb haskell\nsudo -u postgres psql\npostgres=# alter user markw encrypted password 'test1';\npostgres=# \\q\npsql -U markw haskell\npsql (9.5.4)\nType \"help\" for help.\nhaskell=# create table customers (id int, name text, email text);\nCREATE TABLE\nhaskell=#\ninsert into customers values (1, 'Acme Cement', 'info@acmecement.com');\nINSERT 0 1\nhaskell=# \\q\nIf you are not familiar with using Postgres then take a minute to experiment with using the psql\ncommand line utility to connect to the database you just created and peform practice queries:\nmarkw=# \\c haskell\nYou are now connected to database \"haskell\" as user \"markw\".\nhaskell=# \\d\nList of relations\nSchema |\nName\n| Type\n| Owner\n--------+-----------+-------+-------\npublic | customers | table | markw\npublic | links\n| table | markw\npublic | products\n| table | markw\n(3 rows)\nhaskell=# select * from customers;\nid |\nname\n|\nemail\n----+-----------------+---------------------\n1 | Acme Cement\n| info@acmecement.com\n2 | Biff Home Sales | info@biff.com\n3 | My Pens\n| info@mypens.com\n(3 rows)\nhaskell=# select * from products;\nid |\nname\n| cost\n----+---------------+------\n1 | Cement bag\n|\n2.5\n2 | Cheap Pen\n|\n1.5\n3 | Expensive Pen | 14.5\n\nUsing Relational Databases\n118\n(3 rows)\nhaskell=# select * from links;\nid | customer_id | productid\n----+-------------+-----------\n1 |\n1 |\n1\n2 |\n3 |\n2\n3 |\n3 |\n3\n(3 rows)\nhaskell=#\nYou can change default database settings using ConnectInfo:\nConnectInfo\nconnectHost :: String\nconnectPort :: Word16\nconnectUser :: String\nconnectPassword :: String\nconnectDatabase :: String\nIn the following example on lines 9-10 I use defaultConnectInfo that lets me override just some\nsettings, leaving the rest set at default values. The code to access a database using simple-postgresql\nis similar to that in the last section, with a few API changes.\n1\n{-# LANGUAGE OverloadedStrings #-}\n2\n3\nmodule Main where\n4\n5\nimport Database.PostgreSQL.Simple\n6\n7\nmain :: IO ()\n8\nmain = do\n9\nconn <- connect defaultConnectInfo { connectDatabase = \"haskell\",\n10\nconnectUser = \"markw\" }\n11\n-- start by getting table names in database:\n12\ndo\n13\nr <- query_ conn \"SELECT name FROM customers\" :: IO [(Only String)]\n14\nprint \"names and emails in table 'customers' in database haskell:\"\n15\nmapM_ (print . fromOnly) r\n16\n17\n-- add a row to table 'test' and then print out the rows in table 'test':\n\nUsing Relational Databases\n119\n18\ndo\n19\nlet rows :: [(Int, String, String)]\n20\nrows = [(4, \"Mary Smith\", \"marys@acme.com\")]\n21\nexecuteMany conn\n22\n\"INSERT INTO customers (id, name, email) VALUES (?,?,?)\" rows\n23\nr2 <- query_ conn \"SELECT * from customers\" :: IO [(Int, String, String)]\n24\nprint \"number of rows in table 'customers':\"\n25\nprint (length r2)\n26\nprint \"rows in table 'customers':\"\n27\nmapM_ print\nr2\n28\n29\nclose conn\nThe type Only used in line 20 acts as a container for a single value and is defined in the simple-\npostgresql library. It can also be used to pass values for queries like:\nr <- query_ conn \"SELECT name FROM customers where id = ?\" (Only 4::Int)\nThe monad mapping function mapM_ using in line 22 is like mapM but is used when we do not\nneed the resulting collection from executing the map operation. mapM_ is used for side effects, in\nthis case extracting the value for a collection of Only values and printing them. I removed some\noutput from building the example in the following listing:\n$ Database-postgres git:(master) > stack build --exec TestPostgres1\nTestDatabase-0.1.0.0: build\nPreprocessing executable 'TestPostgres1' for TestDatabase-0.1.0.0...\n[1 of 1] Compiling Main\n( TestPostgres1.hs,\n\"names and emails in table 'customers' in database haskell:\"\n\"Acme Cement\"\n\"Biff Home Sales\"\n\"My Pens\"\n\"number of rows in table 'customers':\"\n4\n\"rows in table 'customers':\"\n(1,\"Acme Cement\",\"info@acmecement.com\")\n(2,\"Biff Home Sales\",\"info@biff.com\")\n(3,\"My Pens\",\"info@mypens.com\")\n(4,\"Mary Smith\",\"marys@acme.com\")\nPostgres is my default database and I use it unless there is a compelling reason not to. While work\nfor specific customers has mandated using alternative data stores (e.g., BigTable while working at\nGoogle and MongoDB at Compass Labs), Postgres supports relational tables, free text search, and\nstructured data like JSON.\n\nHaskell Program to Play the Blackjack\nCard Game\nFor much of my work using Haskell I deal mostly with pure code with smaller bits of impure code\nfor network and file IO, etc. Realizing that my use case for using Haskell (mostly pure code) may\nnot be typical, I wanted the last example “cookbook recipe” in this book to be an example dealing\nwith changing state, a program to play the Blackjack card game.\nThe game state is maintained in the type Table that holds information on a randomized deck of cards,\nthe number of players in addition to the game user and the card dealer, the cards in the current hand,\nand the number of betting chips that all players own. Table data is immutable so all of the major\ngame playing functions take a table and any other required inputs, and generate a new table as the\nfunction result.\nThis example starts by asking how many players, besides the card dealer and the game user, should\nplay a simulated Blackjack game. The game user controls when they want another card while the\ndealer and any other simulated players play automatically (they always hit when their card score is\nless than 17).\nI define the types for playing cards and an entire card deck in the file Card.hs:\n1\nmodule Card (Card, Rank, Suit, orderedCardDeck, cardValue) where\n2\n3\nimport Data.Maybe (fromMaybe)\n4\nimport Data.List (elemIndex)\n5\nimport Data.Map (fromList, lookup, keys)\n6\n7\ndata Card = Card { rank :: Rank\n8\n, suit :: Suit }\n9\nderiving (Eq, Show)\n10\n11\ndata Suit = Hearts | Diamonds | Clubs | Spades\n12\nderiving (Eq, Show, Enum, Ord)\n13\n14\ndata Rank = Two | Three | Four\n15\n| Five | Six | Seven | Eight\n16\n| Nine | Ten | Jack\n| Queen | King | Ace\n17\nderiving (Eq, Show, Enum, Ord)\n18\n19\nrankMap = fromList [(Two,2), (Three,3), (Four,4), (Five,5),\n\nHaskell Program to Play the Blackjack Card Game\n121\n20\n(Six,6), (Seven,7), (Eight,8), (Nine,9),\n21\n(Ten,10), (Jack,10), (Queen,10),\n22\n(King,10), (Ace,11)]\n23\n24\norderedCardDeck :: [Card]\n25\norderedCardDeck = [Card rank suit | rank <- keys rankMap,\n26\nsuit <- [Hearts .. Clubs]]\n27\n28\ncardValue :: Card -> Int\n29\ncardValue aCard =\n30\ncase (Data.Map.lookup (rank aCard) rankMap) of\n31\nJust n -> n\n32\nNothing -> 0 -- should never happen\nAs usual, the best way to understand this code is to go to the GHCi repl:\n1\n*Main Card RandomizedList Table> :l Card\n2\n[1 of 1] Compiling Card\n( Card.hs, interpreted )\n3\nOk, modules loaded: Card.\n4\n*Card> :t orderedCardDeck\n5\norderedCardDeck :: [Card]\n6\n*Card> orderedCardDeck\n7\n[Card {rank = Two, suit = Hearts},Card {rank = Two, suit = Diamonds},Card {rank = Tw\\\n8\no, suit = Clubs},Card {rank = Three, suit = Hearts},Card {rank = Three,\n9\n...\n10\n*Card> head orderedCardDeck\n11\nCard {rank = Two, suit = Hearts}\n12\n*Card> cardValue $ head orderedCardDeck\n13\n2\nSo, we have a sorted deck of cards and a utility function for returning the numerical value of a card\n(we always count ace cards as 11 points, deviating from standard Blackjack rules).\nThe next thing we need to get is randomly shuffled lists. The Haskell Wiki³⁷ has a good writeup on\nrandomizing list elements and we are borrowing their function randomizedList (you can see the\nsource code in the file RandomizedList.hs). Here is a sample use:\n³⁷https://wiki.haskell.org/Random_shuffle\n\nHaskell Program to Play the Blackjack Card Game\n122\n1\n*Card> :l RandomizedList.hs\n2\n[1 of 1] Compiling RandomizedList\n( RandomizedList.hs, interpreted )\n3\nOk, modules loaded: RandomizedList.\n4\n*RandomizedList> import Card\n5\n*RandomizedList Card> randomizedList orderedCardDeck\n6\n[Card {rank = Queen, suit = Hearts},Card {rank = Six, suit = Diamonds},Card {rank = \\\n7\nFive, suit = Clubs},Card {rank = Five, suit = Diamonds},Card {rank = Seven, suit = C\\\n8\nlubs},Card {rank = Three, suit = Hearts},Card {rank = Four, suit = Diamonds},Card {r\\\n9\nank = Ace, suit = Hearts},\n10\n...\nMuch of the complexity in this example is implemented in Table.hs which defines the type Table\nand several functions to deal and score hands of dealt cards:\n• createNewTable :: Players -> Table. Players is the integer number of other players at the table.\n• setPlayerBet :: Int -> Table -> Table. Given a new value to bet and a table, generate a new\nmodified table.\n• showTable :: Table -> [Char]. Given a table, generate a string describing the table (in a format\nuseful for development)\n• initialDeal :: [Card] -> Table -> Int -> Table. Given a randomized deck of cards, a table, and the\nnumber of other players, generate a new table.\n• changeChipStack :: Int -> Int -> Table -> Table. Given a player index (index order: user, dealer,\nand other players), a new number of betting chips for the player, and a table, then generate a\nnew modified table.\n• setCardDeck :: [Card] -> Table -> Table. Given a randomized card deck and a table, generate a\nnew table containing the new randomized card list; all other table data is unchanged.\n• dealCards :: Table -> [Int] -> Table. Given a table and a list of player indices for players wanting\nanother card, generate a new modified table.\n• resetTable :: [Card] -> Table -> Int -> Table. Given a new randomized card deck, a table, and a\nnew number of other players, generate a new table.\n• scoreHands :: Table -> Table. Given a table, score all dealt hands and generate a new table with\nthese scores. There is no table type score data, rather, we “score” by changing the number of\nchips all of the players (inclding the dealer) has.\n• dealCardToUser :: Table -> Int -> Table. For the game user, always deal a card. For the dealer\nand other players, deal another card if their hand score is less than 17.\n• handOver :: Table -> Bool. Determine if the current hand is over.\n• setPlayerPasses :: Table -> Table. Call this function when the payer passes. Other players and\ndealer are then played out automatically.\nThe implementation in the file Table.hs is fairly simple, with the exception of the use of Haskell\nlenses to access nested data in the table type. I will discuss the use of lenses after the program listing,\nbut: as you are reading the code look out for variables starting with the underscore character _ that\nalerts the Lens system that it should create data accessors for these variables:\n\nHaskell Program to Play the Blackjack Card Game\n123\n1\n{-# LANGUAGE TemplateHaskell #-}\n-- for makeLens\n2\n3\nmodule Table (Table (..), createNewTable, setPlayerBet, showTable, initialDeal,\n4\nchangeChipStack, setCardDeck, dealCards, resetTable, scoreHands,\n5\ndealCardToUser, handOver, setPlayerPasses) where\n6\n-- note: export dealCardToUser only required for ghci development\n7\n8\nimport Control.Lens\n9\n10\nimport Card\n11\nimport Data.Bool\n12\nimport Data.Maybe (fromMaybe)\n13\n14\ndata Table = Table { _numPlayers :: Int\n15\n, _chipStacks :: [Int] -- number of chips,\n16\n-- indexed by player index\n17\n, _dealtCards :: [[Card]] -- dealt cards for user,\n18\n-- dealer, and other players\n19\n, _currentPlayerBet :: Int\n20\n, _userPasses\n:: Bool\n21\n, _cardDeck\n:: [Card]\n22\n}\n23\nderiving (Show)\n24\n25\ntype Players = Int\n26\n27\ncreateNewTable :: Players -> Table\n28\ncreateNewTable n =\n29\nTable n\n30\n[500 | _ <- [1 .. n]] -- give each player (incuding dealer) 10 chips\n31\n[[] | _ <- [0..n]] -- dealt cards for user and other players\n32\n-- (we don't track dealer's chips)\n33\n20 -- currentPlayerBet number of betting chips\n34\nFalse\n35\n[] -- placeholder for random shuffled card deck\n36\n37\nresetTable :: [Card] -> Table -> Int -> Table\n38\nresetTable cardDeck aTable numberOfPlayers =\n39\nTable numberOfPlayers\n40\n(_chipStacks aTable)\n-- using Lens accessor\n41\n[[] | _ <- [0..numberOfPlayers]]\n42\n(_currentPlayerBet aTable) -- using Lens accessor\n43\nFalse\n\nHaskell Program to Play the Blackjack Card Game\n124\n44\ncardDeck\n45\n46\n-- Use lens extensions for type Table:\n47\n48\nmakeLenses ''Table\n49\n50\nshowDealtCards :: [[Card]] -> String\n51\nshowDealtCards dc =\n52\n(show [map cardValue hand | hand <- dc])\n53\n54\nsetCardDeck :: [Card] -> Table -> Table\n55\nsetCardDeck newDeck =\n56\nover cardDeck (\\_ -> newDeck)\n-- change value to new card deck\n57\n58\ndealCards :: Table -> [Int] -> Table\n59\ndealCards aTable playerIndices =\n60\nlast $ scanl dealCardToUser aTable playerIndices\n61\n62\ninitialDeal cardDeck aTable numberOfPlayers =\n63\ndealCards\n64\n(dealCards (resetTable cardDeck aTable numberOfPlayers)\n65\n[0 .. numberOfPlayers])\n66\n[0 .. numberOfPlayers]\n67\n68\nshowTable :: Table -> [Char]\n69\nshowTable aTable =\n70\n\"\\nCurrent table data:\\n\" ++\n71\n\"\nChipstacks: \" ++\n72\n\"\\n\nPlayer: \" ++ (show (head (_chipStacks aTable))) ++\n73\n\"\\n\nOther players: \" ++ (show (tail (_chipStacks aTable))) ++\n74\n\"\\n\nUser cards: \" ++ (show (head (_dealtCards aTable))) ++\n75\n\"\\n\nDealer cards: \" ++ (show ((_dealtCards aTable) !! 1)) ++\n76\n\"\\n\nOther player's cards: \" ++ (show (tail (tail(_dealtCards aTable)))) ++\n77\n-- \"\\n\nDealt cards: \" ++ (show (_dealtCards aTable)) ++\n78\n\"\\n\nDealt card values: \" ++ (showDealtCards (_dealtCards aTable)) ++\n79\n\"\\n\nCurrent player bet: \" ++\n80\n(show (_currentPlayerBet aTable)) ++\n81\n\"\\n\nPlayer pass: \" ++\n82\n(show (_userPasses aTable)) ++ \"\\n\"\n83\n84\nclipScore aTable playerIndex =\n85\nlet s = score aTable playerIndex in\n86\nif s < 22 then s else 0\n\nHaskell Program to Play the Blackjack Card Game\n125\n87\n88\nscoreHands aTable =\n89\nlet chipStacks2 = _chipStacks aTable\n90\nplayerScore = clipScore aTable 0\n91\ndealerScore = clipScore aTable 1\n92\notherScores = map (clipScore aTable) [2..]\n93\nnewPlayerChipStack = if playerScore > dealerScore then\n94\n(head chipStacks2) + (_currentPlayerBet aTable)\n95\nelse\n96\nif playerScore < dealerScore then\n97\n(head chipStacks2) - (_currentPlayerBet aTable)\n98\nelse (head chipStacks2)\n99\nnewOtherChipsStacks =\n100\nmap (\\(x,y) -> if x > dealerScore then\n101\ny + 20\n102\nelse\n103\nif x < dealerScore then\n104\ny - 20\n105\nelse y)\n106\n(zip otherScores (tail chipStacks2))\n107\nnewChipStacks\n= newPlayerChipStack:newOtherChipsStacks\n108\nin\n109\nover chipStacks (\\_ -> newChipStacks) aTable\n110\n111\nsetPlayerBet :: Int -> Table -> Table\n112\nsetPlayerBet newBet =\n113\nover currentPlayerBet (\\_ -> newBet)\n114\n115\nsetPlayerPasses :: Table -> Table\n116\nsetPlayerPasses aTable =\n117\nlet numPlayers = _numPlayers aTable\n118\nplayerIndices = [1..numPlayers]\n119\nt1 = over userPasses (\\_ -> True) aTable\n120\nt2 = dealCards t1 playerIndices\n121\nt3 = dealCards t2 playerIndices\n122\nt4 = dealCards t3 playerIndices\n123\nin\n124\nt4\n125\n126\n127\nchangeChipStack :: Int -> Int -> Table -> Table\n128\nchangeChipStack playerIndex newValue =\n129\nover chipStacks (\\a -> a & element playerIndex .~ newValue)\n\nHaskell Program to Play the Blackjack Card Game\n126\n130\n131\nscoreOLD aTable playerIndex =\n132\nlet scores = map cardValue ((_dealtCards aTable) !! playerIndex)\n133\ntotalScore = sum scores in\n134\nif totalScore < 22 then totalScore else 0\n135\n136\nscore aTable playerIndex =\n137\nlet scores = map cardValue ((_dealtCards aTable) !! playerIndex)\n138\ntotalScore = sum scores in\n139\ntotalScore\n140\n141\ndealCardToUser' :: Table -> Int -> Table\n142\ndealCardToUser' aTable playerIndex =\n143\nlet nextCard = head $ _cardDeck aTable\n144\nplayerCards = nextCard : ((_dealtCards aTable) !! playerIndex)\n145\nnewTable = over cardDeck (\\cd -> tail cd) aTable in\n146\nover dealtCards (\\a -> a & element playerIndex .~ playerCards) newTable\n147\n148\ndealCardToUser :: Table -> Int -> Table\n149\ndealCardToUser aTable playerIndex\n150\n| playerIndex == 0\n= dealCardToUser' aTable playerIndex -- user\n151\n| otherwise\n= if (score aTable playerIndex) < 17 then\n152\ndealCardToUser' aTable playerIndex\n153\nelse aTable\n154\n155\nhandOver :: Table -> Bool\n156\nhandOver aTable =\n157\n_userPasses aTable\nIn line 48 we use the function makeLenses to generate access functions for the type Table. We will\nlook in some detail at lines 54-56 where we use the lense over function to modify a nested value in\na table, returning a new table:\n1\nsetCardDeck :: [Card] -> Table -> Table\n2\nsetCardDeck newDeck =\n3\nover cardDeck (\\_ -> newDeck)\nThe expression in line 3 evaluates to a partial function that takes another argument, a table, and\nreturns a new table with the card deck modified. Function over expects a function as its second\nargument. In this example, the inline function ignores the argument it is called with, which would\nbe the old card deck value, and returns the new card deck value which is placed in the table value.\nUsing lenses can greatly simplify the code to manipulate complex types.\n\nHaskell Program to Play the Blackjack Card Game\n127\nAnother place where I am using lenses is in the definition of function scoreHands (lines 88-109). On\nline 109 we are using the over function to replace the old player betting chip counts with the new\nvalue we have just calculated:\nover chipStacks (\\_ -> newChipStacks) aTable\nSimilarly, we use over in line 113 to change the current player bet. In function handOver on line\n157, notice how I am using the generated function _userPasses to extract the value of the user passes\nboolean flag from a table.\nThe function main, defined in the file Main.hs, uses the code we have just seen to represent a table\nand modify a table, is fairly simple. A main game loop repetitively accepts game user imput, and\ncalls the appropriate functions to modify the current table, producing a new table. Remember that\nthe table data is immutable: we always generate a new table from the old table when we need to\nmodify it.\n1\nmodule Main where\n2\n3\nimport Card\n-- pure code\n4\nimport Table\n-- pure code\n5\nimport RandomizedList\n-- impure code\n6\n7\nprintTable :: Table -> IO ()\n8\nprintTable aTable =\n9\nputStrLn $ showTable aTable\n10\n11\nrandomDeck =\n12\nrandomizedList orderedCardDeck\n13\n14\ngameLoop :: Table -> Int -> IO b\n15\ngameLoop aTable numberOfPlayers = do\n16\nprintTable aTable\n17\ncardDeck <- randomDeck\n18\nif (handOver aTable) then\n19\ndo\n20\nputStrLn \"\\nHand over. State of table at the end of the game:\\n\"\n21\nprintTable aTable\n22\nputStrLn \"\\nNewly dealt hand:\\n\"\n23\ngameLoop (initialDeal cardDeck (scoreHands aTable)\n24\nnumberOfPlayers)\n25\nnumberOfPlayers\n26\nelse\n27\ndo\n\nHaskell Program to Play the Blackjack Card Game\n128\n28\nputStrLn \"Enter command:\"\n29\nputStrLn \"\nh)it or set bet to 10, 20, 30; any other key to stay:\"\n30\ncommand <- getLine\n31\nif elem command [\"10\", \"20\", \"30\"] then\n32\ngameLoop (setPlayerBet (read command) aTable) numberOfPlayers\n33\nelse\n34\nif command == \"h\" then\n35\ngameLoop (dealCards aTable [0 .. numberOfPlayers]) numberOfPlayers\n36\nelse\n37\ngameLoop (setPlayerPasses (dealCards aTable [1 .. numberOfPlayers]))\n38\nnumberOfPlayers\n39\n-- player stays (no new cards)\n40\n41\nmain :: IO b\n42\nmain = do\n43\nputStrLn \"Start a game of Blackjack. Besides yourself, how many other\"\n44\nputStrLn \"players do you want at the table?\"\n45\ns <- getLine\n46\nlet num = (read s :: Int) + 1\n47\ncardDeck <- randomDeck\n48\nlet aTable = initialDeal cardDeck (createNewTable num) num\n49\ngameLoop aTable num\nI encourage you to try playing the game yourself, but if you don’t here is a sample game:\n1\n*Main Card RandomizedList Table> main\n2\nStart a game of Blackjack. Besides yourself, how many other\n3\nplayers do you want at the table?\n4\n1\n5\n6\nCurrent table data:\n7\nChipstacks:\n8\nPlayer: 500\n9\nOther players: [500]\n10\nUser cards: [Card {rank = Three, suit = Clubs},Card {rank = Two, suit = Hearts}]\n11\nDealer cards: [Card {rank = Queen, suit = Diamonds},Card {rank = Seven, suit = Clu\\\n12\nbs}]\n13\nOther player's cards: [[Card {rank = King, suit = Hearts},Card {rank = Six, suit =\\\n14\nDiamonds}]]\n15\nDealt card values: [[3,2],[10,7],[10,6]]\n16\nCurrent player bet: 20\n17\nPlayer pass: False\n18\n\nHaskell Program to Play the Blackjack Card Game\n129\n19\nEnter command: h)it or set bet to 10, 20, 30; any other key to stay:\n20\nh\n21\n22\nCurrent table data:\n23\nChipstacks:\n24\nPlayer: 500\n25\nOther players: [500]\n26\nUser cards: [Card {rank = Six, suit = Hearts},Card {rank = Three, suit = Clubs},Ca\\\n27\nrd {rank = Two, suit = Hearts}]\n28\nDealer cards: [Card {rank = Queen, suit = Diamonds},Card {rank = Seven, suit = Clu\\\n29\nbs}]\n30\nOther player's cards: [[Card {rank = Eight, suit = Hearts},Card {rank = King, suit\\\n31\n= Hearts},Card {rank = Six, suit = Diamonds}]]\n32\nDealt card values: [[6,3,2],[10,7],[8,10,6]]\n33\nCurrent player bet: 20\n34\nPlayer pass: False\n35\n36\nEnter command: h)it or set bet to 10, 20, 30; any other key to stay:\n37\nh\n38\n39\nCurrent table data:\n40\nChipstacks:\n41\nPlayer: 500\n42\nOther players: [500]\n43\nUser cards: [Card {rank = King, suit = Clubs},Card {rank = Six, suit = Hearts},Car\\\n44\nd {rank = Three, suit = Clubs},Card {rank = Two, suit = Hearts}]\n45\nDealer cards: [Card {rank = Queen, suit = Diamonds},Card {rank = Seven, suit = Clu\\\n46\nbs}]\n47\nOther player's cards: [[Card {rank = Eight, suit = Hearts},Card {rank = King, suit\\\n48\n= Hearts},Card {rank = Six, suit = Diamonds}]]\n49\nDealt card values: [[10,6,3,2],[10,7],[8,10,6]]\n50\nCurrent player bet: 20\n51\nPlayer pass: False\n52\n53\nEnter command: h)it or set bet to 10, 20, 30; any other key to stay:\n54\n55\nCurrent table data:\n56\nChipstacks:\n57\nPlayer: 500\n58\nOther players: [500]\n59\nUser cards: [Card {rank = King, suit = Clubs},Card {rank = Six, suit = Hearts},Car\\\n60\nd {rank = Three, suit = Clubs},Card {rank = Two, suit = Hearts}]\n61\nDealer cards: [Card {rank = Queen, suit = Diamonds},Card {rank = Seven, suit = Clu\\\n\nHaskell Program to Play the Blackjack Card Game\n130\n62\nbs}]\n63\nOther player's cards: [[Card {rank = Eight, suit = Hearts},Card {rank = King, suit\\\n64\n= Hearts},Card {rank = Six, suit = Diamonds}]]\n65\nDealt card values: [[10,6,3,2],[10,7],[8,10,6]]\n66\nCurrent player bet: 20\n67\nPlayer pass: True\n68\n69\nHand over. State of table at the end of the game:\n70\n71\nCurrent table data:\n72\nChipstacks:\n73\nPlayer: 520\n74\nOther players: [520]\n75\nUser cards: [Card {rank = King, suit = Clubs},Card {rank = Six, suit = Hearts},Car\\\n76\nd {rank = Three, suit = Clubs},Card {rank = Two, suit = Hearts}]\n77\nDealer cards: [Card {rank = Queen, suit = Diamonds},Card {rank = Seven, suit = Clu\\\n78\nbs}]\n79\nOther player's cards: [[Card {rank = Eight, suit = Hearts},Card {rank = King, suit\\\n80\n= Hearts},Card {rank = Six, suit = Diamonds}]]\n81\nDealt card values: [[10,6,3,2],[10,7],[8,10,6]]\n82\nCurrent player bet: 20\n83\nPlayer pass: True\nHere the game user has four cards with values of [10,6,3,2] for a winning score of 21. The dealer has\n[10,7] for a score of 17 and the other player has [8,10,6], a value greater than 21 so the player went\n“bust.”\nI hope that you enjoyed this last example that demonstrates a reasonable approach for managing\nstate when using immutable data.\n\nSection 3 - Larger Projects\nThis section is new for the second edition of this book. So far we have covered the basics of Haskell\nprogramming and seen many examples. In this section we look at a few new projects that I derived\nfrom my own work and these new examples will hopefully further encourage you to think of novel\nuses for Haskell in your own work.\nThe project knowledge_graph_creator helps to automate the process of creating Knowledge\nGraphs from raw text input and generates data for both the Neo4J open source graph database\nas well as RDF data for use in semantic web and linked data applications. I have also implemented\nthis same application in Common Lisp that is also a new example in the latest edition of my book\nLoving Common Lisp, Or The Savvy Programmer’s Secret Weapon³⁸ (released September 2019).\nThe next two chapters in this section are similar in that they both use examples of using Python for\nNatural Language Processing (NLP) tasks, wrapping the Python code as a REST service, and then\nwriting Haskell clients for these services.\nThe project HybridHaskellPythonNlp uses web services written in Python for natural language\nprocessing. The Python web services use the SpaCy library.\nThe project HybridHaskellPythonCorefAnaphoraResolution uses web services written in Python\nto allow Haskell applications to use deep learning models created with TensorFlow and Keras.\nIn these last two examples I use REST APIs to access code written in Python. A good alternative that\nI don’t cover in this book is using the servant library³⁹ for generating distributed applications.\n³⁸https://leanpub.com/lovinglisp\n³⁹https://www.servant.dev/\n\nKnowledge Graph Creator\nThe large project described here processes raw text inputs and generates data for knowledge graphs\nin formats for both the Neo4J graph database and in RDF format for semantic web and linked data\napplications.\nThis application works by identifying entities in text. Example entity types are people, companies,\ncountry names, city names, broadcast network names, political party names, and university names.\nWe saw earlier code for detecting entities in the chapter on natural language processing (NLP) and\nwe will reuse this code. We will discuss later three strategies for reusing code from different projects.\nThe following figure shows part of a Neo4J Knowledge Graph created with the example code. This\ngraph has shortened labels in displayed nodes but Neo4J offers a web browser-based console that lets\nyou interactively explore Knowledge Graphs. We don’t cover setting up Neo4J here so please use the\nNeo4J documentation⁴⁰. As an introduction to RDF data, the semantic web, and linked data you can\nget free copies of my two books Practical Semantic Web and Linked Data Applications, Common\nLisp Edition⁴¹ and Practical Semantic Web and Linked Data Applications, Java, Scala, Clojure, and\nJRuby Edition⁴².\nPart of a Knowledge Graph shown in Neo4J web application console\nThere are two versions of this project that deal with generating duplicate data in two ways:\n• As either Neo4J Cypher data or RDF triples data are created, store generated data in a SQLite\nembedded database. Check this database before writing new output data.\n• Ignore the problem of generating duplicate data and filter out duplicates in the outer processing\npipeline that uses the Knowledge Graph Creator as one processing step.\n⁴⁰https://neo4j.com/docs/operations-manual/current/introduction/\n⁴¹http://markwatson.com/opencontentdata/book_lisp.pdf\n⁴²http://markwatson.com/opencontentdata/book_java.pdf\n\nKnowledge Graph Creator\n133\nFor my own work I choose the second method since filtering duplicates is as easy as a few Makefile\ntargets (the following listing is in the file Makefile in the directory haskell_tutorial_cookbook_-\nexamples/knowledge_graph_creator_pure):\nall: gendata rdf cypher\ngendata:\nstack build --fast --exec Dev-exe\nrdf:\necho \"Removing duplicate RDF statements\"\nawk '!visited[$$0]++' out.n3 > output.n3\nrm -f out.n3\ncypher:\necho \"Removing duplicate Cypher statements\"\nawk '!visited[$$0]++' out.cypher > output.cypher\nrm -f out.cypher\nThe Haskell KGCreator application we develop here writes output files out.n3 (N3 is a RDF data\nformat) and out.cypher (Cypher is the import output format and query language for the Neo4J open\nsource and commercial graph database). The awk commands remove duplicate lines and write de-\nduplicated data to output.n3 and output.cypher.\nWe will use this second approach but the next section provides sufficient information and a link to\nalternative code in case you are interested in using SQLite to prevent duplicate data generation.\nNotes for Using SQLite to Avoid Duplicates (Optional Material)\nWe saw two methods of avoiding duplicates in generated data in the last section. If you want to\nuse the first method for avoiding generating duplicate data, I leave it as an exercise but here are\nsome notes to get you started: you can then modify the example code by using the utility function\nBlackboard.h in the directory knowledge_graph_creator_pure/src/fileutils and implement the\nlogic seen below for checking new generated data to see if it is in the SQLite database. This first\nmethod as it also is a good example for wrapping the embedded SQLite library in an IO Monad and\nis left as an exercise, otherwise skip this section.\nBefore you write either an RDF statement or a Neo4J Cypher data import statement, check to see if\nthe statement has already been written using something like:\n\nKnowledge Graph Creator\n134\ncheck <- blackboard_check_key new_data_uri\nif check\n....\nand after writing a RDF statement or a Neo4J Cypher data import statement, write it to the\ntemportary SQLite database using something like:\nblackboard_write newStatementString\nFor the rest of the chapter we will use the approach of not keeping track of generated data in SQLite\nand instead remove duplicates during postprocessing using the standard awk command line utility.\nThis section is optional. In the rest of this chapter we use the example code in knowledge_graph_-\ncreator_pure.\nCode Layout For the KGCreator Project and strategies\nfor sharing Haskell code between projects\nWe will reuse the code for finding entities that we studied in an earlier chapter. There are several\nways to reuse code from multiple local Haskell projects:\n• In a project’s cabal file, use relative paths to the source code for other projects. This is\nmy preferred way to work but has the drawback that the stack command sdist to make a\ndistribution tarball will not work with relative paths. If this is a problem for you then create\nrelative symbolic file links to the source directories in other projects.\n• In your project’s stack.yaml file, add the other project’s name and path as a extra-deps.\n• In library projects, define a packages definition and install the library globally on your system.\nI almost always use the first method on my projects with dependencies on other local projects I work\non and this is also the approach we use here. The relevant lines in the file KGCreator.cabal are:\n1\nlibrary\n2\nexposed-modules:\n3\nCorefWebClient\n4\nNlpWebClient\n5\nClassificationWebClient\n6\nDirUtils\n7\nFileUtils\n8\nBlackBoard\n9\nGenTriples\n10\nGenNeo4jCypher\n\nKnowledge Graph Creator\n135\n11\nApis\n12\nCategorize\n13\nNlpUtils\n14\nSummarize\n15\nEntities\n16\nother-modules:\n17\nPaths_KGCreator\n18\nBroadcastNetworkNamesDbPedia\n19\nCategory1Gram\n20\nCategory2Gram\n21\nCityNamesDbpedia\n22\nCompanyNamesDbpedia\n23\nCountryNamesDbpedia\n24\nPeopleDbPedia\n25\nPoliticalPartyNamesDbPedia\n26\nSentence\n27\nStemmer\n28\nTradeUnionNamesDbPedia\n29\nUniversityNamesDbPedia\n30\n31\nhs-source-dirs:\n32\nsrc\n33\nsrc/webclients\n34\nsrc/fileutils\n35\nsrc/sw\n36\nsrc/toplevel\n37\n../NlpTool/src/nlp\n38\n../NlpTool/src/nlp/data\nThis is a standard looking cabal file except for lines 37 and 38 where the source paths reference\nthe example code for the NlpTool application developed in a previous chapter. The exposed module\nBlackBoard (line 8) is not used but I leave it in the cabal file in case you want to experiment with\nrecording generated data in SQLite to avoid data duplication. You are likely to also want to use\nBlackBoard if you modify this example to continuously process incoming data in a production\nsystem. This is left as an exercise.\nBefore going into too much detail on the implementation let’s look at the layout of the project code:\n\nKnowledge Graph Creator\n136\n1\nsrc/fileutils:\n2\nBlackBoard.hs\nDirUtils.hs\nFileUtils.hs\n3\n4\n../NlpTool/src/nlp:\n5\nCategorize.hs\nEntities.hs\nNlpUtils.hs\nSentence.hs\nStemmer.hs\nSumm\n6\n7\n../NlpTool/src/nlp/data:\n8\nBroadcastNetworkNamesDbPedia.hs\nCompanyNamesDbpedia.hs\nTradeUnionNamesDbPedia\n9\nCategory1Gram.hs\nCountryNamesDbpedia.hs\nUniversityNamesDbPedia.hs\n10\nCategory2Gram.hs\nPeopleDbPedia.hs\n11\nCityNamesDbpedia.hs\nPoliticalPartyNamesDbPedia.hs\n12\n13\nsrc/sw:\n14\nGenNeo4jCypher.hs\nGenTriples.hs\n15\n16\nsrc/toplevel:\n17\nApis.hs\nAs mentioned before, we are using the Haskell source fies in a relative path ../NlpTool/src/… and\nthe local src directory. We discuss this code in the next few sections.\nThe Main Event: Detecting Entities in Text\nA primary task in KGCreator is to identify entities (people, places, etc.) in text and then we will\ncreate RDF and Neo4J Cypher data statements using these entities, knowledge of the origin of text\ndata and general relationships between entities.\nWe will use the top level code that we developed earlier that is located in the directory ../NlpTool/s-\nrc/nlp (please see the chapter Natural Language Processing Tools for more detail):\n• Categorize.hs - categorizes text into categories like news, religion, business, politics, science,\netc.\n• Entities.hs - identifies entities like people, companies, places, new broadcast networks, labor\nunions, etc. in text\n• Summarize.hs - creates an extractive summary of text\nThe KGCreator Haskell application looks in a specified directory for text files to process. For each\nfile with a .txt extension there should be a matching file with the extension .meta that contains a\nsingle line: the URI of the web location where the corresponding text was found. The reason we need\nthis is that we want to create graph knowledge data from information found in text sources and the\noriginal location of the data is important to preserve. In other words, we want to know where the\ndata elements in our knowledge graph came from.\n\nKnowledge Graph Creator\n137\nWe have not looked at an example of using command line arguments yet so let’s go into some detail\non how we do this. Previously when we have defined an output target executable in our .cabal file,\nin this case KGCreator-exe, we could use stack to build the executable and run it with:\nstack build --fast --exec KGCreator-exe\"\nNow, we have an executable that requires two arguments: a source input directory and the file\nroot for generated RDF and Cypher output files. We can pass command line arguments using this\nnotation:\nstack build --fast --exec \"KGCreator-exe test_data outtest\"\nThe two command line arguments are:\n• test_data which is the file path of a local directory containing the input files\n• outtest which is the root file name for generated Neo4J Cypher and RDF output files\nIf you are using KGCreator in production, then you will want to copy the compiled and linked\nexecutable file KGCreator-exe to somewhere on your PATH like /usr/local/bin.\nThe following listing shows the file app/Main.hs, the main program for this example that handles\ncommand line arguments and calls two top level functions in src/toplevel/Apis.hs:\n1\nmodule Main where\n2\n3\nimport System.Environment (getArgs)\n4\nimport Apis (processFilesToRdf, processFilesToNeo4j)\n5\n6\nmain :: IO ()\n7\nmain = do\n8\nargs <- getArgs\n9\ncase args of\n10\n[] -> error \"must supply an input directory containing text and meta files\"\n11\n[_] -> error \"in addition to an input directory, also specify a root file name f\\\n12\nor the generated RDF and Cypher files\"\n13\n[inputDir, outputFileRoot] -> do\n14\nprocessFilesToRdf\ninputDir $ outputFileRoot ++ \".n3\"\n15\nprocessFilesToNeo4j inputDir $ outputFileRoot ++ \".cypher\"\n16\n_ -> error \"too many arguments\"\nHere we use getArgs in line8 to fetch a list of command line arguments and verify that at\nleast two arguments have been provided. Then we call the functions processFilesToRdf and\nprocessFilesToNeo4j and the functions they call in the next three sections.\n\nKnowledge Graph Creator\n138\nUtility Code for Generating RDF\nThe code for generating RDF and for generating Neo4J Cypher data is similar. We start with the code\nto generate RDF triples. Before we look at the code, let’s start with a few lines of generated RDF:\n<http://dbpedia.org/resource/The_Wall_Street_Journal>\n<http://knowledgebooks.com/schema/aboutCompanyName>\n\"Wall Street Journal\" .\n<https://newsshop.com/june/z902.html>\n<http://knowledgebooks.com/schema/containsCountryDbPediaLink>\n<http://dbpedia.org/resource/Canada> .\nThe next listing shows the file src/sw/GenTriples.hs that finds entities like broadcast network\nnames, city names, company names, people’s names, political party names, and university names in\ntext and generates RDF triple data. If you need to add more entity types for your own applications,\nthen use the following steps:\n• Look at the format of entity data for the NlpTool example and add names for the new entity\ntype you are adding.\n• Add a utility function to find instances of the new entity type to NlpTools. For example, if\nyou are adding a new entity type “park names”, then copy the code for companyNames to\nparkNames, modify as necessary, and export parkNames.\n• In the following code, add new code for the new entity helper function after lines 10, 97, 151,\nand 261. Use the code for companyNames as an example.\nThe map *category_to_uri_map** created in lines 36 to 84 maps a topic name to a linked Data URI\nthat describes the topic. For example, we would not refer to an information source as being about\nthe topic “economics”, but would instead refer to a linked data URI like http://knowledgebooks.\ncom/schema/topic/economics. The utility function uri_from_categor takes a text description of a\ntopic like “economy” and converts it to an appropriate URI using the map *category_to_uri_map**.\nThe utility function textToTriple takes a file path to a text input file and a path to meta file path,\ncalculates the text string representing the generated triples for the input text file, and returns the\nresult wrapped in an IO monad.\n\nKnowledge Graph Creator\n139\n1\nmodule GenTriples\n2\n( textToTriples\n3\n, category_to_uri_map\n4\n) where\n5\n6\nimport Categorize (bestCategories)\n7\nimport Entities\n8\n( broadcastNetworkNames\n9\n, cityNames\n10\n, companyNames\n11\n, countryNames\n12\n, peopleNames\n13\n, politicalPartyNames\n14\n, tradeUnionNames\n15\n, universityNames\n16\n)\n17\nimport FileUtils\n18\n( MyMeta\n19\n, filePathToString\n20\n, filePathToWordTokens\n21\n, readMetaFile\n22\n, uri\n23\n)\n24\nimport Summarize (summarize, summarizeS)\n25\n26\nimport qualified Data.Map as M\n27\nimport Data.Maybe (fromMaybe)\n28\n29\ngenerate_triple :: [Char] -> [Char] -> [Char] -> [Char]\n30\ngenerate_triple s p o = s ++ \"\n\" ++ p ++ \"\n\" ++ o ++ \" .\\n\"\n31\n32\nmake_literal :: [Char] -> [Char]\n33\nmake_literal s = \"\\\"\" ++ s ++ \"\\\"\"\n34\n35\ncategory_to_uri_map :: M.Map [Char] [Char]\n36\ncategory_to_uri_map =\n37\nM.fromList\n38\n[ (\"news_weather\", \"<http://knowledgebooks.com/schema/topic/weather>\")\n39\n, (\"news_war\", \"<http://knowledgebooks.com/schema/topic/war>\")\n40\n, (\"economics\", \"<http://knowledgebooks.com/schema/topic/economics>\")\n41\n, (\"news_economy\", \"<http://knowledgebooks.com/schema/topic/economics>\")\n42\n, (\"news_politics\", \"<http://knowledgebooks.com/schema/topic/politics>\")\n43\n, (\"religion\", \"<http://knowledgebooks.com/schema/topic/religion>\")\n\nKnowledge Graph Creator\n140\n44\n, ( \"religion_buddhism\"\n45\n, \"<http://knowledgebooks.com/schema/topic/religion/buddhism>\")\n46\n, ( \"religion_islam\"\n47\n, \"<http://knowledgebooks.com/schema/topic/religion/islam>\")\n48\n, ( \"religion_christianity\"\n49\n, \"<http://knowledgebooks.com/schema/topic/religion/christianity>\")\n50\n, ( \"religion_hinduism\"\n51\n, \"<http://knowledgebooks.com/schema/topic/religion/hinduism>\")\n52\n, ( \"religion_judaism\"\n53\n, \"<http://knowledgebooks.com/schema/topic/religion/judaism>\")\n54\n, (\"chemistry\", \"<http://knowledgebooks.com/schema/topic/chemistry>\")\n55\n, (\"computers\", \"<http://knowledgebooks.com/schema/topic/computers>\")\n56\n, (\"computers_ai\", \"<http://knowledgebooks.com/schema/topic/computers/ai>\")\n57\n, ( \"computers_ai_datamining\"\n58\n, \"<http://knowledgebooks.com/schema/topic/computers/ai/datamining>\")\n59\n, ( \"computers_ai_learning\"\n60\n, \"<http://knowledgebooks.com/schema/topic/computers/ai/learning>\")\n61\n, ( \"computers_ai_nlp\"\n62\n, \"<http://knowledgebooks.com/schema/topic/computers/ai/nlp>\")\n63\n, ( \"computers_ai_search\"\n64\n, \"<http://knowledgebooks.com/schema/topic/computers/ai/search>\")\n65\n, ( \"computers_ai_textmining\"\n66\n, \"<http://knowledgebooks.com/schema/topic/computers/ai/textmining>\")\n67\n, ( \"computers/programming\"\n68\n, \"<http://knowledgebooks.com/schema/topic/computers/programming>\")\n69\n, ( \"computers_microsoft\"\n70\n, \"<http://knowledgebooks.com/schema/topic/computers/microsoft>\")\n71\n, ( \"computers/programming/ruby\"\n72\n, \"<http://knowledgebooks.com/schema/topic/computers/programming/ruby>\")\n73\n, ( \"computers/programming/lisp\"\n74\n, \"<http://knowledgebooks.com/schema/topic/computers/programming/lisp>\")\n75\n, (\"health\", \"<http://knowledgebooks.com/schema/topic/health>\")\n76\n, ( \"health_exercise\"\n77\n, \"<http://knowledgebooks.com/schema/topic/health/exercise>\")\n78\n, ( \"health_nutrition\"\n79\n, \"<http://knowledgebooks.com/schema/topic/health/nutrition>\")\n80\n, (\"mathematics\", \"<http://knowledgebooks.com/schema/topic/mathematics>\")\n81\n, (\"news_music\", \"<http://knowledgebooks.com/schema/topic/music>\")\n82\n, (\"news_physics\", \"<http://knowledgebooks.com/schema/topic/physics>\")\n83\n, (\"news_sports\", \"<http://knowledgebooks.com/schema/topic/sports>\")\n84\n]\n85\n86\nuri_from_category :: [Char] -> [Char]\n\nKnowledge Graph Creator\n141\n87\nuri_from_category key =\n88\nfromMaybe (\"\\\"\" ++ key ++ \"\\\"\") $ M.lookup key category_to_uri_map\n89\n90\ntextToTriples :: FilePath -> [Char] -> IO [Char]\n91\ntextToTriples file_path meta_file_path = do\n92\nword_tokens <- filePathToWordTokens file_path\n93\ncontents <- filePathToString file_path\n94\nputStrLn $ \"** contents:\\n\" ++ contents ++ \"\\n\"\n95\nmeta_data <- readMetaFile meta_file_path\n96\nlet people = peopleNames word_tokens\n97\nlet companies = companyNames word_tokens\n98\nlet countries = countryNames word_tokens\n99\nlet cities = cityNames word_tokens\n100\nlet broadcast_networks = broadcastNetworkNames word_tokens\n101\nlet political_parties = politicalPartyNames word_tokens\n102\nlet trade_unions = tradeUnionNames word_tokens\n103\nlet universities = universityNames word_tokens\n104\nlet a_summary = summarizeS contents\n105\nlet the_categories = bestCategories word_tokens\n106\nlet filtered_categories =\n107\nmap (uri_from_category . fst) $\n108\nfilter (\\(name, value) -> value > 0.3) the_categories\n109\nputStrLn \"\\nfiltered_categories:\"\n110\nprint filtered_categories\n111\n--putStrLn \"a_summary:\"\n112\n--print a_summary\n113\n--print $ summarize contents\n114\n115\nlet summary_triples =\n116\ngenerate_triple\n117\n(uri meta_data)\n118\n\"<http://knowledgebooks.com/schema/summaryOf>\" $\n119\n\"\\\"\" ++ a_summary ++ \"\\\"\"\n120\nlet category_triples =\n121\nconcat\n122\n[ generate_triple\n123\n(uri meta_data)\n124\n\"<http://knowledgebooks.com/schema/news/category/>\"\n125\ncat\n126\n| cat <- filtered_categories\n127\n]\n128\nlet people_triples1 =\n129\nconcat\n\nKnowledge Graph Creator\n142\n130\n[ generate_triple\n131\n(uri meta_data)\n132\n\"<http://knowledgebooks.com/schema/containsPersonDbPediaLink>\"\n133\n(snd pair)\n134\n| pair <- people\n135\n]\n136\nlet people_triples2 =\n137\nconcat\n138\n[ generate_triple\n139\n(snd pair)\n140\n\"<http://knowledgebooks.com/schema/aboutPersonName>\"\n141\n(make_literal (fst pair))\n142\n| pair <- people\n143\n]\n144\nlet company_triples1 =\n145\nconcat\n146\n[ generate_triple\n147\n(uri meta_data)\n148\n\"<http://knowledgebooks.com/schema/containsCompanyDbPediaLink>\"\n149\n(snd pair)\n150\n| pair <- companies\n151\n]\n152\nlet company_triples2 =\n153\nconcat\n154\n[ generate_triple\n155\n(snd pair)\n156\n\"<http://knowledgebooks.com/schema/aboutCompanyName>\"\n157\n(make_literal (fst pair))\n158\n| pair <- companies\n159\n]\n160\nlet country_triples1 =\n161\nconcat\n162\n[ generate_triple\n163\n(uri meta_data)\n164\n\"<http://knowledgebooks.com/schema/containsCountryDbPediaLink>\"\n165\n(snd pair)\n166\n| pair <- countries\n167\n]\n168\nlet country_triples2 =\n169\nconcat\n170\n[ generate_triple\n171\n(snd pair)\n172\n\"<http://knowledgebooks.com/schema/aboutCountryName>\"\n\nKnowledge Graph Creator\n143\n173\n(make_literal (fst pair))\n174\n| pair <- countries\n175\n]\n176\nlet city_triples1 =\n177\nconcat\n178\n[ generate_triple\n179\n(uri meta_data)\n180\n\"<http://knowledgebooks.com/schema/containsCityDbPediaLink>\"\n181\n(snd pair)\n182\n| pair <- cities\n183\n]\n184\nlet city_triples2 =\n185\nconcat\n186\n[ generate_triple\n187\n(snd pair)\n188\n\"<http://knowledgebooks.com/schema/aboutCityName>\"\n189\n(make_literal (fst pair))\n190\n| pair <- cities\n191\n]\n192\nlet bnetworks_triples1 =\n193\nconcat\n194\n[ generate_triple\n195\n(uri meta_data)\n196\n\"<http://knowledgebooks.com/schema/containsBroadCastDbPediaLink>\"\n197\n(snd pair)\n198\n| pair <- broadcast_networks\n199\n]\n200\nlet bnetworks_triples2 =\n201\nconcat\n202\n[ generate_triple\n203\n(snd pair)\n204\n\"<http://knowledgebooks.com/schema/aboutBroadCastName>\"\n205\n(make_literal (fst pair))\n206\n| pair <- broadcast_networks\n207\n]\n208\nlet pparties_triples1 =\n209\nconcat\n210\n[ generate_triple\n211\n(uri meta_data)\n212\n\"<http://knowledgebooks.com/schema/containsPoliticalPartyDbPediaLink>\"\n213\n(snd pair)\n214\n| pair <- political_parties\n215\n]\n\nKnowledge Graph Creator\n144\n216\nlet pparties_triples2 =\n217\nconcat\n218\n[ generate_triple\n219\n(snd pair)\n220\n\"<http://knowledgebooks.com/schema/aboutPoliticalPartyName>\"\n221\n(make_literal (fst pair))\n222\n| pair <- political_parties\n223\n]\n224\nlet unions_triples1 =\n225\nconcat\n226\n[ generate_triple\n227\n(uri meta_data)\n228\n\"<http://knowledgebooks.com/schema/containsTradeUnionDbPediaLink>\"\n229\n(snd pair)\n230\n| pair <- trade_unions\n231\n]\n232\nlet unions_triples2 =\n233\nconcat\n234\n[ generate_triple\n235\n(snd pair)\n236\n\"<http://knowledgebooks.com/schema/aboutTradeUnionName>\"\n237\n(make_literal (fst pair))\n238\n| pair <- trade_unions\n239\n]\n240\nlet universities_triples1 =\n241\nconcat\n242\n[ generate_triple\n243\n(uri meta_data)\n244\n\"<http://knowledgebooks.com/schema/containsUniversityDbPediaLink>\"\n245\n(snd pair)\n246\n| pair <- universities\n247\n]\n248\nlet universities_triples2 =\n249\nconcat\n250\n[ generate_triple\n251\n(snd pair)\n252\n\"<http://knowledgebooks.com/schema/aboutTradeUnionName>\"\n253\n(make_literal (fst pair))\n254\n| pair <- universities\n255\n]\n256\nreturn $\n257\nconcat\n258\n[ people_triples1\n\nKnowledge Graph Creator\n145\n259\n, people_triples2\n260\n, company_triples1\n261\n, company_triples2\n262\n, country_triples1\n263\n, country_triples2\n264\n, city_triples1\n265\n, city_triples2\n266\n, bnetworks_triples1\n267\n, bnetworks_triples2\n268\n, pparties_triples1\n269\n, pparties_triples2\n270\n, unions_triples1\n271\n, unions_triples2\n272\n, universities_triples1\n273\n, universities_triples2\n274\n, category_triples\n275\n, summary_triples\n276\n]\nThe code in this file could be shortened but having repetitive code for each entity type hopefully\nmakes it easier for you to understand how it works.\nUtility Code for Generating Cypher Input Data for\nNeo4J\nNow we will generate Neo4J Cypher data. In order to keep the implementation simple, both the RDF\nand Cypher generation code starts with raw text and performs the NLP analysis to find entities. This\nexample could be refactored to perform the NLP analysis just one time but in practice you will likely\nbe working with either RDF or NEO4J and so you will probably extract just the code you need from\nthis example (i.e., either the RDF or Cypher generation code).\nBefore we look at the code, let’s start with a few lines of generated Neo4J Cypher import data:\n\nKnowledge Graph Creator\n146\nCREATE (newsshop_com_june_z902_html_news)-[:ContainsCompanyDbPediaLink]->(Wall_Stree\\\nt_Journal)\nCREATE (Canada:Entity {name:\"Canada\", uri:\"<http://dbpedia.org/resource/Canada>\"})\nCREATE (newsshop_com_june_z902_html_news)-[:ContainsCountryDbPediaLink]->(Canada)\nCREATE (summary_of_abcnews_go_com_US_violent_long_lasting_tornadoes_threaten_oklahom\\\na_texas_storyid63146361:Summary {name:\"summary_of_abcnews_go_com_US_violent_long_las\\\nting_tornadoes_threaten_oklahoma_texas_storyid63146361\", uri:\"<https://abcnews.go.co\\\nm/US/violent-long-lasting-tornadoes-threaten-oklahoma-texas/story?id=63146361>\", sum\\\nmary:\"Part of the system that delivered severe weather to the central U.S. over the \\\nweekend is moving into the Northeast today, producing strong to severe storms -- dam\\\naging winds, hail or isolated tornadoes can't be ruled out. Severe weather is foreca\\\nst to continue on Tuesday, with the western storm moving east into the Midwest and p\\\narts of the mid-Mississippi Valley.\"})\nThe following listing shows the file src/sw/GenNeo4jCypher.hs. This code is very similar to the\ncode for generating RDF in the last section. The same notes for adding your own new entity notes\nin the last section are also relevant here.\nNotice that we import in line 29 the map category_to_uri_map that was defined in the last section.\nThe function neo4j_category_node_defs defined in lines 35 to 43 creates category graph nodes\nfor each category in the map category_to_uri_map. These nodes will be referenced by graph\nnodes created in the functions create_neo4j_node, create_neo4j_lin, create_summary_node,\nand create_entity_node. The top level function is textToCypher that is similar to the function\ntextToTriples in the last section.\n1\n{-# LANGUAGE OverloadedStrings #-}\n2\n3\nmodule GenNeo4jCypher\n4\n( textToCypher\n5\n, neo4j_category_node_defs\n6\n) where\n7\n8\nimport Categorize (bestCategories)\n9\nimport Data.List (isInfixOf)\n10\nimport Data.Char (toLower)\n11\nimport Data.String.Utils (replace)\n12\nimport Entities\n13\n( broadcastNetworkNames\n14\n, cityNames\n15\n, companyNames\n16\n, countryNames\n17\n, peopleNames\n18\n, politicalPartyNames\n\nKnowledge Graph Creator\n147\n19\n, tradeUnionNames\n20\n, universityNames\n21\n)\n22\nimport FileUtils\n23\n( MyMeta\n24\n, filePathToString\n25\n, filePathToWordTokens\n26\n, readMetaFile\n27\n, uri\n28\n)\n29\nimport GenTriples (category_to_uri_map)\n30\nimport Summarize (summarize, summarizeS)\n31\n32\nimport qualified Data.Map as M\n33\nimport Data.Maybe (fromMaybe)\n34\nimport Database.SQLite.Simple\n35\n36\n-- for debug:\n37\nimport Data.Typeable (typeOf)\n38\n39\nneo4j_category_node_defs :: [Char]\n40\nneo4j_category_node_defs =\n41\nreplace\n42\n\"/\"\n43\n\"_\"\n44\n$ concat\n45\n[ \"CREATE (\" ++ c ++ \":CategoryType {name:\\\"\" ++ c ++ \"\\\"})\\n\"\n46\n| c <- M.keys category_to_uri_map\n47\n]\n48\n49\nuri_from_category :: p -> p\n50\nuri_from_category s = s -- might want the full version from GenTriples\n51\n52\nrepl :: Char -> Char\n53\nrepl '-' = '_'\n54\nrepl '/' = '_'\n55\nrepl '.' = '_'\n56\nrepl c = c\n57\n58\nfilterChars :: [Char] -> [Char]\n59\nfilterChars = filter (\\c -> c /= '?' && c /= '=' && c /= '<' && c /= '>')\n60\n61\ncreate_neo4j_node :: [Char] -> ([Char], [Char])\n\nKnowledge Graph Creator\n148\n62\ncreate_neo4j_node uri =\n63\nlet name =\n64\n(map repl (filterChars\n65\n(replace \"https://\" \"\" (replace \"http://\" \"\" uri)))) ++\n66\n\"_\" ++\n67\n(map toLower node_type)\n68\nnode_type =\n69\nif isInfixOf \"dbpedia\" uri\n70\nthen \"DbPedia\"\n71\nelse \"News\"\n72\nnew_node =\n73\n\"CREATE (\" ++\n74\nname ++ \":\" ++\n75\nnode_type ++ \" {name:\\\"\" ++ (replace \" \" \"_\" name) ++\n76\n\"\\\", uri:\\\"\" ++ uri ++ \"\\\"})\\n\"\n77\nin (name, new_node)\n78\n79\ncreate_neo4j_link :: [Char] -> [Char] -> [Char] -> [Char]\n80\ncreate_neo4j_link node1 linkName node2 =\n81\n\"CREATE (\" ++ node1 ++ \")-[:\" ++ linkName ++ \"]->(\" ++ node2 ++ \")\\n\"\n82\n83\ncreate_summary_node :: [Char] -> [Char] -> [Char]\n84\ncreate_summary_node uri summary =\n85\nlet name =\n86\n\"summary_of_\" ++\n87\n(map repl $\n88\nfilterChars (replace \"https://\" \"\" (replace \"http://\" \"\" uri)))\n89\ns1 = \"CREATE (\" ++ name ++ \":Summary {name:\\\"\" ++ name ++ \"\\\", uri:\\\"\"\n90\ns2 = uri ++ \"\\\", summary:\\\"\" ++ summary ++ \"\\\"})\\n\"\n91\nin s1 ++ s2\n92\n93\ncreate_entity_node :: ([Char], [Char]) -> [Char]\n94\ncreate_entity_node entity_pair =\n95\n\"CREATE (\" ++ (replace \" \" \"_\" (fst entity_pair)) ++\n96\n\":Entity {name:\\\"\" ++ (fst entity_pair) ++ \"\\\", uri:\\\"\" ++\n97\n(snd entity_pair) ++ \"\\\"})\\n\"\n98\n99\ncreate_contains_entity :: [Char] -> [Char] -> ([Char], [Char]) -> [Char]\n100\ncreate_contains_entity relation_name source_uri entity_pair =\n101\nlet new_person_node = create_entity_node entity_pair\n102\nnew_link = create_neo4j_link source_uri\n103\nrelation_name\n104\n(replace \" \" \"_\" (fst entity_pair))\n\nKnowledge Graph Creator\n149\n105\nin\n106\n(new_person_node ++ new_link)\n107\n108\nentity_node_helper :: [Char] -> [Char] -> [([Char], [Char])] -> [Char]\n109\nentity_node_helper relation_name node_name entity_list =\n110\nconcat [create_contains_entity\n111\nrelation_name node_name entity | entity <- entity_list]\n112\n113\ntextToCypher :: FilePath -> [Char] -> IO [Char]\n114\ntextToCypher file_path meta_file_path = do\n115\nlet prelude_nodes = neo4j_category_node_defs\n116\nputStrLn \"+++++++++++++++++ prelude node defs:\"\n117\nprint prelude_nodes\n118\nword_tokens <- filePathToWordTokens file_path\n119\ncontents <- filePathToString file_path\n120\nputStrLn $ \"** contents:\\n\" ++ contents ++ \"\\n\"\n121\nmeta_data <- readMetaFile meta_file_path\n122\nputStrLn \"++ meta_data:\"\n123\nprint meta_data\n124\nlet people = peopleNames word_tokens\n125\nlet companies = companyNames word_tokens\n126\nputStrLn \"^^^^ companies:\"\n127\nprint companies\n128\nlet countries = countryNames word_tokens\n129\nlet cities = cityNames word_tokens\n130\nlet broadcast_networks = broadcastNetworkNames word_tokens\n131\nlet political_parties = politicalPartyNames word_tokens\n132\nlet trade_unions = tradeUnionNames word_tokens\n133\nlet universities = universityNames word_tokens\n134\nlet a_summary = summarizeS contents\n135\nlet the_categories = bestCategories word_tokens\n136\nlet filtered_categories =\n137\nmap (uri_from_category . fst) $\n138\nfilter (\\(name, value) -> value > 0.3) the_categories\n139\nputStrLn \"\\nfiltered_categories:\"\n140\nprint filtered_categories\n141\nlet (node1_name, node1) = create_neo4j_node (uri meta_data)\n142\nlet summary1 = create_summary_node (uri meta_data) a_summary\n143\nlet category1 =\n144\nconcat\n145\n[ create_neo4j_link node1_name \"Category\" cat\n146\n| cat <- filtered_categories\n147\n]\n\nKnowledge Graph Creator\n150\n148\nlet pp = entity_node_helper \"ContainsPersonDbPediaLink\" node1_name people\n149\nlet cmpny = entity_node_helper \"ContainsCompanyDbPediaLink\" node1_name companies\n150\nlet cntry = entity_node_helper \"ContainsCountryDbPediaLink\" node1_name countries\n151\nlet citys = entity_node_helper \"ContainsCityDbPediaLink\" node1_name cities\n152\nlet bnet = entity_node_helper \"ContainsBroadcastNetworkDbPediaLink\"\n153\nnode1_name broadcast_networks\n154\nlet ppart = entity_node_helper \"ContainsPoliticalPartyDbPediaLink\"\n155\nnode1_name political_parties\n156\nlet tunion = entity_node_helper \"ContainsTradeUnionDbPediaLink\"\n157\nnode1_name trade_unions\n158\nlet uni = entity_node_helper \"ContainsUniversityDbPediaLink\"\n159\nnode1_name universities\n160\nreturn $ concat [node1, summary1, category1, pp, cmpny, cntry, citys, bnet,\n161\nppart, tunion, uni]\nBecause the top level function is textToCypher returns a string wrapped in a monad, it is possible to\nadd “debug”” print statements in textToCypher. I left many such debug statements in the example\ncode to help you understand the data that is being operated on. I leave it as an exercise to remove\nthese print statements if you use this code in your own projects and no longer need to see the debug\noutput.\nTop Level API Code for Handling Knowledge Graph\nData Generation\nSo far we have looked at processing command line arguments and processing individual input\nfiles. Now we look at higher level utility APIs for processing an entire directory of input files. The\nfollowing listing shows the file API.hs that contains the two top level helper functions we saw in\napp/Main.hs.\nThe functions processFilesToRdf and processFilesToNeo4j both have the function type signature\nFilePath->FilePath->IO() and are very similar except for calling different helper functions to\ngenerate RDF triples or Cypher input graph data:\n\nKnowledge Graph Creator\n151\n1\nmodule Apis\n2\n( processFilesToRdf\n3\n, processFilesToNeo4j\n4\n) where\n5\n6\nimport FileUtils\n7\nimport GenNeo4jCypher\n8\nimport GenTriples (textToTriples)\n9\n10\nimport qualified Database.SQLite.Simple as SQL\n11\n12\nimport Control.Monad (mapM)\n13\nimport Data.String.Utils (replace)\n14\nimport System.Directory (getDirectoryContents)\n15\n16\nimport Data.Typeable (typeOf)\n17\n18\nprocessFilesToRdf :: FilePath -> FilePath -> IO ()\n19\nprocessFilesToRdf dirPath outputRdfFilePath = do\n20\nfiles <- getDirectoryContents dirPath :: IO [FilePath]\n21\nlet filtered_files = filter isTextFile files\n22\nlet full_paths = [dirPath ++ \"/\" ++ fn | fn <- filtered_files]\n23\nputStrLn \"full_paths:\"\n24\nprint full_paths\n25\nlet r =\n26\n[textToTriples fp1 (replace \".txt\" \".meta\" fp1)\n27\n|\n28\nfp1 <- full_paths] :: [IO [Char]]\n29\ntripleL <-\n30\nmapM (\\fp -> textToTriples fp (replace \".txt\" \".meta\" fp)) full_paths\n31\nlet tripleS = concat tripleL\n32\nputStrLn tripleS\n33\nwriteFile outputRdfFilePath tripleS\n34\n35\nprocessFilesToNeo4j :: FilePath -> FilePath -> IO ()\n36\nprocessFilesToNeo4j dirPath outputRdfFilePath = do\n37\nfiles <- getDirectoryContents dirPath :: IO [FilePath]\n38\nlet filtered_files = filter isTextFile files\n39\nlet full_paths = [dirPath ++ \"/\" ++ fn | fn <- filtered_files]\n40\nputStrLn \"full_paths:\"\n41\nprint full_paths\n42\nlet prelude_node_defs = neo4j_category_node_defs\n43\nputStrLn\n\nKnowledge Graph Creator\n152\n44\n(\"+++++\ntype of prelude_node_defs is: \" ++\n45\n(show (typeOf prelude_node_defs)))\n46\nprint prelude_node_defs\n47\ncypher_dataL <-\n48\nmapM (\\fp -> textToCypher fp (replace \".txt\" \".meta\" fp)) full_paths\n49\nlet cypher_dataS = concat cypher_dataL\n50\nputStrLn cypher_dataS\n51\nwriteFile outputRdfFilePath $ prelude_node_defs ++ cypher_dataS\nSince both of these functions return IO monads, I could add “debug” print statements that should be\nhelpful in understanding the data being operated on.\nWrapup for Automating the Creation of Knowledge\nGraphs\nThe code in this chapter will provide you with a good start for creating both test knowledge graphs\nand for generating data for production. In practice, generated data should be reviewed before use\nand additional data manually generated as needed. It is good practice to document required manual\nchanges because this documentation can be used in the requirements for updating the code in this\nchapter to more closely match your knowledge graph requirements.\n\nHybrid Haskell and Python Natural\nLanguage Processing\nHere we will write a Haskell client for using a Natural Language Processing (NLP) server written\nin Python. There is some common material in this chapter and the next chapter Hybrid Haskell and\nPython For Coreference Resolution because I wanted both chapters to be self contained.\nExample Use of the Haskell NLP Client\nBefore learning how to use the Python NLP server code and understand the code for the Haskell\nclient code, let’s look at an example of running the client code so you understand the type of\nprocessing that we are performing:\n1\n$ stack build --fast --exec HybridHaskellPythonNlp-exe\n2\nEnter text (all on one line)\n3\nJohn Smith went to Mexico to see the Pepsi plant\n4\nresponse from NLP server:\n5\nNlpResponse {entities = [\"John Smith/PERSON\",\"Mexico/GPE\",\"Pepsi/ORG\"],\n6\ntokens = [\"John\",\"Smith\",\"went\",\"to\",\"Mexico\",\"to\",\"see\",\"the\",\"Pepsi\",\\\n7\n\"plant\"]}\n8\nEnter text (all on one line)\nNotice on line 5 that each of the three entities is tagged with the entity type. GPE is the tag for a\ncountry and the tag ORG can refer to an entity that is a company or a non-profit organization.\nThere is some overlap in functionality between the Python SpaCy NLP library and my pure Haskell\ncode in the NLP Tools chapter. SpaCy has the advantage of using state of the art deep learning\nmodels.\nSetting up the Python NLP Server\nI assume that you have some familiarity with using Python. If not, you will still be able to follow these\ndirections assuming that you have the utilities pip, and python installed. I recommend installing\nPython and Pip using Anaconda⁴³.\n⁴³https://anaconda.org/anaconda/conda\n\nHybrid Haskell and Python Natural Language Processing\n154\nThe server code is in the subdirectory HybridHaskellPythonNlp/python_spacy_nlp_server where\nyou will work when performing a one time initialization. After the server is installed you can then\nrun it from the command line from any directory on your laptop.\nI recommend that you use virtual Python environments when using Python applications to separate\nthe dependencies required for each application or development project. Here I assume that you are\nrunning in a Python version 3.6 (or higher) version environment. First install the dependencies:\n1\npip install -U spacy\n2\npython -m spacy download en\n3\npip install falcon\nThen change directory to the subdirectory HybridHaskellPythonNlp/python_spacy_nlp_server\nand install the NLP server:\n1\ncd HybridHaskellPythonNlp/python_spacy_nlp_server\n2\npython setup.py install\nOnce you install the server, you can run it from any directory on your laptop or server using:\n1\nspacynlpserver\nI use deep learning models written in Python using TensorFlow or PyTorch in applications I write\nin Haskell or Common Lisp. While it is possible to directly embed models in Haskell and Common\nLisp, I find it much easier and developer friendly to wrap deep learning models I use a REST services\nas I have done here. Often deep learning models only require about a gigabyte of memory and\nusing pre-trained models has lightweight CPU resource needs so while I am developing on my\nlaptop I might have two or three models running and available as wrapped REST services. For\nproduction, I configure both the Python services and my Haskell and Common Lisp applications\nto start automatically on system startup.\nThis is not a Python programming book and I will not discuss the simple Python wrapping code but\nif you are also a Python developer you can easily read and understand the code.\nUnderstanding the Haskell NLP Client Code\nThe Python server returns JSON file. We saw earlier the use of the Haskell aeson library for parsing\nJSON data stored as a string into Haskell native data. We also used the wreq library to access remote\nweb services. We use both of these libraries here:\n\nHybrid Haskell and Python Natural Language Processing\n155\n1\n{-# LANGUAGE OverloadedStrings #-}\n2\n{-# LANGUAGE DeriveDataTypeable #-}\n3\n4\n-- reference: http://www.serpentine.com/wreq/tutorial.html\n5\nmodule NlpWebClient\n6\n( nlpClient, NlpResponse\n7\n) where\n8\n9\nimport Control.Lens\n10\nimport Data.ByteString.Lazy.Char8 (unpack)\n11\nimport Data.Maybe (fromJust)\n12\nimport Network.URI.Encode as E -- encode is also in Data.Aeson\n13\nimport Network.Wreq\n14\n15\nimport Text.JSON.Generic\n16\n17\ndata NlpResponse = NlpResponse {entities::[String], tokens::[String]} deriving (Show\\\n18\n, Data, Typeable)\n19\n20\nbase_url = \"http://127.0.0.1:8008?text=\"\n21\n22\nnlpClient :: [Char] -> IO NlpResponse\n23\nnlpClient query = do\n24\nputStrLn $ \"\\n\\n***\nProcessing \" ++ query\n25\nr <- get $ base_url ++ (E.encode query) ++ \"&no_detail=1\"\n26\nlet ret = (decodeJSON (unpack (fromJust (r ^? responseBody)))) :: NlpResponse\n27\nreturn ret\nThe main command line program for using the client library:\nmodule Main where\nimport NlpWebClient\nmain :: IO ()\nmain = do\nputStrLn \"Enter text (all on one line)\"\ns <- getLine\nresponse <- (nlpClient s) :: IO NlpResponse\nputStr \"response from NLP server:\\n\"\nputStrLn $ show response\nmain\n\nHybrid Haskell and Python Natural Language Processing\n156\nWrapup for Using the Python SpaCy NLP Service\nThe example in this chapter shows a technique that I often use for using libraries and frameworks\nthat are not written in Haskell: wrap the service implemented in another programming language\nis a REST web service. While it is possible to use a foreign function interface (FFI) to call out to\ncode written in other languages I find for my own work that I prefer calling out to a separate\nservice especially when I run other services on remote servers so I do not need to run them on\nmy development laptop. For production it is also useful to be able to easily scale horizontally across\nservers.\n\nHybrid Haskell and Python For\nCoreference Resolution\nHere we will write a Haskell client for using a server written in Python that performs coreference\nresolution (more on this later). There is some common material in this chapter and the last chapter\nHybrid Haskell and Python Natural Language Processing because I wanted both chapters to be self\ncontained. The code for this chapter can be found in the subdirectory HybridHaskellPythonCore-\nfAnaphoraResolution.\nCoreference resolution is also called anaphora resolution and is the process for replacing pronouns\nin text with the original nouns, proper nouns, or noun phrases that the pronouns refer to.\nBefore discussing setting up the Python library for performing coreference analysis and the Haskell\nclient, let’s run the client so you can see and understand anaphora resolution:\n1\n$ stack build --fast --exec HybridHaskellPythonCorefAnaphoraResolution-exe\n2\nEnter text (all on one line)\n3\nJohn Smith drove a car. He liked it.\n4\n5\n6\n***\nProcessing John%20Smith%20drove%20a%20car.%20He%20liked%20it.\n7\nstatus code: 200\n8\ncontent type: Just \"application/text\"\n9\nresponse body: John Smith drove a car. John Smith liked a car.\n10\nresponse from coreference server:\n\"John Smith drove a car. John Smith liked a car.\"\n11\nEnter text (all on one line)\nIn this example notice that the words “He” and “it” in the second sentence are replaced by “John\nSmith” and “a car” which makes it easier to write information extraction applications.\nInstalling the Python Coreference Server\nI recommend that you use virtual Python environments when using Python applications to separate\nthe dependencies required for each application or development project. Here I assume that you\nare running in a Python version 3.6 (or higher) version environment. If you want to install\nthe neuralcoref library using pip you must use and older version of spaCy. First install the\ndependencies:\n\nHybrid Haskell and Python For Coreference Resolution\n158\n1\npip install spacy==2.1.0\n2\npip install neuralcoref\n3\npip install falcon\nAs I write this chapter the neuralcoref model and library require a slightly older version of spaCy\n(the current latest version is 2.3.0).\nIf you want to instead use the latest version of spaCy then install neuralcoref from source:\n1\npip install spacy\n2\ngit clone https://github.com/huggingface/neuralcoref.git\n3\ncd neuralcoref\n4\npython setup.py install\n5\npip install falcon\nAfter installing all dependencies, then change directory to the subdirectory python_coreference_-\nanaphora_resolution_server and install the coref server:\n1\ncd python_coreference_anaphora_resolution_server\n2\npython setup.py install\nOnce you install the server, you can run it from any directory on your laptop or server using:\n1\ncorefserver\nI use deep learning models written in Python using TensorFlow or PyTorch in applications I write\nin Haskell or Common Lisp. While it is possible to directly embed models in Haskell and Common\nLisp, I find it much easier and developer friendly to wrap deep learning models I use a REST services\nas I have done here. Often deep learning models only require about a gigabyte of memory and\nusing pre-trained models has lightweight CPU resource needs so while I am developing on my\nlaptop I might have two or three models running and available as wrapped REST services. For\nproduction, I configure both the Python services and my Haskell and Common Lisp applications\nto start automatically on system startup.\nThis is not a Python programming book and I will not discuss the simple Python wrapping code but\nif you are also a Python developer you can easily read and understand the code.\nUnderstanding the Haskell Coreference Client Code\nThe code for the library for fetching data from the Python service is in the subdirectory src in the\nfile CorefWebClient.hs.\nWe will use techniques for accessing remote web services using the wreq library and using the\nlens library for accessing the response from the Python server. Here the response is plain text with\npronouns replaced by the nouns that they represent. We don’t use the aeson library to parse JSON\ndata as we did in the previous chapter.\n\nHybrid Haskell and Python For Coreference Resolution\n159\n1\n{-# LANGUAGE OverloadedStrings #-}\n2\n3\n-- reference: http://www.serpentine.com/wreq/tutorial.html\n4\nmodule CorefWebClient\n5\n( corefClient\n6\n) where\n7\n8\nimport Control.Lens\n9\nimport Data.ByteString.Lazy.Char8 (unpack)\n10\nimport Data.Maybe (fromJust)\n11\nimport Network.URI.Encode (encode)\n12\nimport Network.Wreq\n13\n14\nbase_url = \"http://127.0.0.1:8000?text=\"\n15\n16\ncorefClient :: [Char] -> IO [Char]\n17\ncorefClient query = do\n18\nputStrLn $ \"\\n\\n***\nProcessing \" ++ (encode query)\n19\nr <- get $ base_url ++ (encode query) ++ \"&no_detail=1\"\n20\nputStrLn $ \"status code: \" ++ (show (r ^. responseStatus . statusCode))\n21\nputStrLn $ \"content type: \" ++ (show (r ^? responseHeader \"Content-Type\"))\n22\nputStrLn $ \"response body: \" ++ (unpack (fromJust (r ^? responseBody)))\n23\nreturn $ unpack (fromJust (r ^? responseBody))\nThe code for the main application is in the subdirectory app in the file Main.hs.\n1\nmodule Main where\n2\n3\nimport CorefWebClient\n4\n5\nmain :: IO ()\n6\nmain = do\n7\nputStrLn \"Enter text (all on one line)\"\n8\ns <- getLine\n9\nresponse <- corefClient s\n10\nputStr \"response from coreference server:\\t\"\n11\nputStrLn $ show response\n12\nmain\n\nHybrid Haskell and Python For Coreference Resolution\n160\nWrapup for Using the Python Coreference NLP Service\nThe example in this chapter is fairly simple but shows a technique that I often use for using\nlibraries and frameworks that are not written in Haskell: wrap the service implemented in another\nprogramming language is a REST web service. While it is possible to use a foreign function interface\n(FFI) to call out to code written in other languages I find for my own work that I prefer calling out\nto a separate service, especially when I run other services on remote servers so I do not need to run\nthem on my development laptop. For production it is also useful to be able to easily scale horizontally\nacross servers.\n\nBook Wrap Up\nAs I mentioned in the Preface, I had a slow start learning Haskell because I tried to learn too much\nat one time. In this book I have attempted to show you a subset of Haskell that is sufficient to write\ninteresting programs - a gentle introduction.\nHaskell beginners often dislike the large error listings from the compiler. The correct attitude is to\nrecognize that these error messages are there to help you. That is easier said than done, but try to be\nhappy when the compiler points out an error - in the long run I find using Haskell’s fussy compiler\nsaves me time and lets me refactor code knowing that if I miss something in my refactoring the\ncompiler will immediately let me know what needs to be fixed.\nThe other thing that I hope you learned working through this book is how effective repl based\nprogramming is. Most code I write, unless it is very trivial, starts its life in a GHCi repl. When you\nare working with somene else’s Haskell code it is similarly useful to have their code loaded in a repl\nas you read.\nI have been programming professionally for forty years and I use many programming languages.\nOnce I worked my way through early difficulties using Haskell it has become a favorite program-\nming language. I hope that you enjoy Haskell development as much as I do.\n\nAppendix A - Haskell Tools Setup\nI recommend that if you are new to Haskell that you at least do a minimal installation of stack and\nwork through the first chapter using an interactive REPL. After experimenting with the REPL then\ndo please come back to Appendix A and install support for the editor of your choice (or an IDE) and\nhlint.\nstack\nI assume that you have the Haskell package manager stack installed. If you have not installed stack\nyet please follow these directions⁴⁴.\nAfter installing stack and running it you will have a directory “.stack” in your home directory\nwhere stack will keep compiled libraries and configuration data. You will want to create a file\n“∼/.stack/config.yaml” with contents similar to my stack configuration file:\n1\ntemplates:\n2\nparams:\n3\nauthor-email: markw@markwatson.com\n4\nauthor-name: Mark Watson\n5\ncategory: dev\n6\ncopyright: Copyright 2016 Mark Watson. All rights reserved\n7\ngithub-username: mark-watson\nReplace my name and email address with yours. You might also want to install the package manager\nCabal and the “lint” program hlint:\n$ stack install cabal-install\n$ stack install hlint\nThese installs might take a while so go outside for ten minutes and get some fresh air.\nYou should get in the habit of running hlint on your code and consider trying to remove all or at\nleast most warnings. You can customize the types of warnings hlint shows: read the documentation\nfor hlint⁴⁵.\n⁴⁴http://docs.haskellstack.org/en/stable/README.html\n⁴⁵https://github.com/ndmitchell/hlint#readme\n\nAppendix A - Haskell Tools Setup\n163\nCreating a New Stack Project\nI have already created stack projects for the examples in this book. When you have worked through\nthem, then please refer to the stack documentation for creating projects⁴⁶.\nEmacs Setup\nThere are several good alternatives to using the Emacs editor:\n• GEdit on Linux\n• TextMate on OS X\n• IntelliJ with the Haskell plugin (all platforms)\nI use all three of these alternatives on occasion, but Emacs with haskell-mode is my favorite\nenvironment. There are instructions for adding haskell-mode to Emacs on the project home page on\ngithub⁴⁷. If you follow these instructions you will have syntax hiliting and Emacs will understand\nHaskell indentation rules.\nDo you want more of an IDE-like Development\nEnvironment?\nI recommend and use the Intero Emacs package⁴⁸ to get auto completions and real time syntax error\nwarnings. Intero is designed to work with stack.\nI add the following to the bottom of my .emacs file:\n(add-hook ‘haskell-mode-hook ‘intero-mode)\nand if Intero is too “heavy weight” for my current project, then I comment out the add-hook\nexpression. Intero can increase the startup time for Emacs for editing Haskell files. That said, I\nalmost always keep Intero enabled in my Emacs environment.\nhlint\nhlint is a wonderful tool for refining your knowledge and use of the Haskell language. After writing\nnew code and checking that it works, then run hlint for suggestions on how to improve your code.\nInstall hlint using:\n⁴⁶https://docs.haskellstack.org/en/stable/README/#start-your-new-project\n⁴⁷https://github.com/haskell/haskell-mode\n⁴⁸https://commercialhaskell.github.io/intero/\n\nAppendix A - Haskell Tools Setup\n164\n1\nstack install hlint\n\n"
  },
  {
    "path": "embedchain_test/data/lovinglisp-2.txt",
    "content": "\nLoving Common Lisp, or the Savvy\nProgrammer’s Secret Weapon\nMark Watson\nThis book is for sale at http://leanpub.com/lovinglisp\nThis version was published on 2023-05-01\nThis is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing\nprocess. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and\nmany iterations to get reader feedback, pivot until you have the right book and build traction once\nyou do.\nThis work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0\nInternational License\n\nContents\nCover Material, Copyright, and License . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n1\nPreface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n2\nNotes on the Eighth Edition Published August 2022 . . . . . . . . . . . . . . . . . . . . . . . .\n2\nNotes on the Seventh Edition Published March 2021 . . . . . . . . . . . . . . . . . . . . . . .\n2\nNotes on the Sixth Edition Published June 2020 . . . . . . . . . . . . . . . . . . . . . . . . . .\n2\nNotes on the Fifth Edition Published September 2019 . . . . . . . . . . . . . . . . . . . . . . .\n3\nWhy Use Common Lisp? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n3\nA Request from the Author . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n3\nOlder Book Editions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n4\nAcknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n5\nSetting Up Your Common Lisp Development System and Quicklisp . . . . . . . . . . . . . .\n5\nIntroduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n7\nWhy Did I Write this Book? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n7\nFree Software Tools for Common Lisp Programming . . . . . . . . . . . . . . . . . . . . . . .\n8\nMaking Book Examples Run Portably on Most Common Lisp Implementations . . . . . . .\n8\nHow is Lisp Different from Languages like Java and C++? . . . . . . . . . . . . . . . . . . . .\n9\nAdvantages of Working in a Lisp Environment . . . . . . . . . . . . . . . . . . . . . . . . . . .\n10\nCommon Lisp Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n11\nGetting Started with SBCL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n11\nMaking the repl Nicer using rlwrap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n13\nThe Basics of Lisp Programming\n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n14\nSymbols . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n20\nOperations on Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n21\nUsing Arrays and Vectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n25\nUsing Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n26\nUsing Hash Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n29\nUsing Eval to Evaluate Lisp Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n33\nUsing a Text Editor to Edit Lisp Source Files . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n33\nRecovering from Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n34\nGarbage Collection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n36\nLoading your Working Environment Quickly . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n36\n\nCONTENTS\nFunctional Programming Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n37\nQuicklisp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n38\nUsing Quicklisp to Find Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n38\nUsing Quicklisp to Configure Emacs and Slime\n. . . . . . . . . . . . . . . . . . . . . . . . . .\n40\nDefining Lisp Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n42\nUsing Lambda Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n44\nUsing Recursion\n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n46\nClosures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n47\nUsing the Function eval . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n48\nDefining Common Lisp Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n49\nExample Macro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n49\nUsing the Splicing Operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n50\nUsing macroexpand-1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n51\nUsing Common Lisp Loop Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n52\ndolist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n52\ndotimes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n52\ndo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n53\nUsing the loop Special Form to Iterate Over Vectors or Arrays . . . . . . . . . . . . . . . . .\n54\nCommon Lisp Package System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n55\nInput and Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n58\nThe Lisp read and read-line Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n58\nLisp Printing Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n61\nPlotting Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n63\nImplementing the Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n63\nPackaging as a Quicklisp Project\n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n65\nCommon Lisp Object System - CLOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n67\nExample of Using a CLOS Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n67\nImplementation of the HTMLstream Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n68\nUsing Defstruct or CLOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n71\nHeuristically Guided Search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n73\nNetwork Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n79\nAn introduction to Drakma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n79\nAn introduction to Hunchentoot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n81\nComplete REST Client Server Example Using JSON for Data Serialization . . . . . . . . . .\n83\nNetwork Programming Wrap Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n86\n\nCONTENTS\nUsing the Microsoft Bing Search APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n87\nGetting an Access Key for Microsoft Bing Search APIs . . . . . . . . . . . . . . . . . . . . . .\n87\nExample Search Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n88\nWrap-up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n90\nAccessing Relational Databases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n91\nDatabase Wrap Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n95\nUsing MongoDB, Solr NoSQL Data Stores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n96\nMongoDB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n96\nA Common Lisp Solr Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101\nNoSQL Wrapup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112\nNatural Language Processing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113\nLoading and Running the NLP Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113\nPart of Speech Tagging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117\nCategorizing Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119\nDetecting People’s Names and Place Names\n. . . . . . . . . . . . . . . . . . . . . . . . . . . . 122\nSummarizing Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123\nText Mining . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126\nInformation Gathering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127\nDBPedia Lookup Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127\nWeb Spiders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130\nUsing Apache Nutch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131\nWrap Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132\nUsing The CL Machine-Learning Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133\nUsing the CLML Data Loading and Access APIs . . . . . . . . . . . . . . . . . . . . . . . . . . 134\nK-Means Clustering of Cancer Data Set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136\nSVM Classification of Cancer Data Set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138\nCLML Wrap Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141\nBackpropagation Neural Networks\n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142\nHopfield Neural Networks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154\nUsing Python Deep Learning Models In Common Lisp With a Web Services Interface . . 161\nSetting up the Python Web Services Used in this Chapter . . . . . . . . . . . . . . . . . . . . 161\nInstalling the spaCY NLP Services\n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161\nInstalling the Coreference NLP Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162\nCommon Lisp Client for the spaCy NLP Web Services . . . . . . . . . . . . . . . . . . . . . . 163\nCommon Lisp Client for the Coreference NLP Web Services . . . . . . . . . . . . . . . . . . 165\nTrouble Shooting Possible Problems - Skip if this Example Works on Your System . . . . . 166\nPython Interop Wrap-up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167\n\nCONTENTS\nUsing the PY4CL Library to Embed Python in Common Lisp . . . . . . . . . . . . . . . . . . . 168\nProject Structure, Building the Python Wrapper, and Running an Example . . . . . . . . . . 168\nImplementation of spacy-py4cl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171\nTrouble Shooting Possible Problems - Skip if this Example Works on Your System . . . . . 171\nWrap-up for Using Py4CL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172\nSemantic Web and Linked Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173\nResource Description Framework (RDF) Data Model . . . . . . . . . . . . . . . . . . . . . . . 174\nExtending RDF with RDF Schema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178\nThe SPARQL Query Language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180\nCase Study: Using SPARQL to Find Information about Board of Directors Members of\nCorporations and Organizations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184\nInstalling the Apache Jena Fuseki RDF Server . . . . . . . . . . . . . . . . . . . . . . . . . . . 186\nCommon Lisp Client Examples for the Apache Jena Fuseki RDF Server . . . . . . . . . . . . 187\nAutomatically Generating Data for Knowledge Graphs . . . . . . . . . . . . . . . . . . . . . . 190\nImplementation Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191\nGenerating RDF Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192\nGenerating Data for the Neo4j Graph Database . . . . . . . . . . . . . . . . . . . . . . . . . . 195\nImplementing the Top Level Application APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . 198\nImplementing The Web Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201\nCreating a Standalone Application Using SBCL . . . . . . . . . . . . . . . . . . . . . . . . . . 203\nAugmenting RDF Triples in a Knowledge Graph Using DBPedia . . . . . . . . . . . . . . . . 204\nKGCreator Wrap Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206\nKnowledge Graph Sampler for Creating Small Custom Knowledge Graphs . . . . . . . . . 207\nKnowledge Graph Navigator Common Library Implementation . . . . . . . . . . . . . . . . . 212\nExample Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213\nProject Configuration and Running the Application . . . . . . . . . . . . . . . . . . . . . . . . 218\nReview of NLP Utilities Used in Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222\nDeveloping Low-Level SPARQL Utilities\n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223\nImplementing the Caching Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225\nUtilities in the Main Library File kgn-common.lisp . . . . . . . . . . . . . . . . . . . . . . . . 226\nWrap-up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233\nKnowledge Graph Navigator Text-Based User Interface . . . . . . . . . . . . . . . . . . . . . . 234\nExample Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234\nText User Interface Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241\nWrap-up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245\nKnowledge Graph Navigator User Interface Using LispWorks CAPI . . . . . . . . . . . . . . 246\nProject Configuration and Running the Application . . . . . . . . . . . . . . . . . . . . . . . . 247\nUtilities to Colorize SPARQL and Generated Output . . . . . . . . . . . . . . . . . . . . . . . 249\n\nCONTENTS\nMain Implementation File kgn-capi-ui.lisp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251\nUser Interface Utilites File user-interface.lisp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257\nUser Interface CAPI Options Panes Definition File option-pane.lisp . . . . . . . . . . . . . . 258\nUsing LispWorks CAPI UI Toolkit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261\nWrap-up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267\nUsing the OpenAI APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268\nUsing the Hugging Face Deep Learning Natural Language Processing APIs . . . . . . . . . 274\nUsing a Local Document Embeddings Vector Database With OpenAI GPT3 APIs for\nSemantically Querying Your Own Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277\nOverview of Local Embeddings Vector Database to Enhance the Use of GPT3 APIs With\nLocal Documents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277\nImplementing a Local Vector Database for Document Embeddings . . . . . . . . . . . . . . 278\nUsing Local Embeddings Vector Database With OpenAI GPT APIs . . . . . . . . . . . . . . 281\nTesting Local Embeddings Vector Database With OpenAI GPT APIs . . . . . . . . . . . . . 282\nAdding Chat History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283\nWrap Up for Using Local Embeddings Vector Database to Enhance the Use of GPT3 APIs\nWith Local Documents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285\nPrompt Engineering for Large Language Models . . . . . . . . . . . . . . . . . . . . . . . . . . . 286\nTwo Types of LLMS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286\nPrompt Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287\nPrompt Engineering Wrapup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293\nUsing Common Lisp with Wolfram/One . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295\nBook Wrapup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300\n\nCover Material, Copyright, and\nLicense\nCopyright 2011-2023 Mark Watson. All rights reserved. This book may be shared using the Creative\nCommons “share and share alike, no modifications, no commercial reuse” license.\nThis eBook will be updated occasionally so please periodically check the leanpub.com web page for\nthis book¹ for updates.\nThis is the eighth edition released August 2022.\nPlease visit the author’s website².\nIf you found a copy of this book on the web and find it of value then please consider buying a copy\nat leanpub.com/lovinglisp³.\nIf you would like to support my work please consider purchasing my books on Leanpub⁴ and star\nmy git repositories that you find useful on GitHub⁵. You can also interact with me on social media\non Mastodon⁶ and Twitter⁷.\n¹https://leanpub.com/lovinglisp\n²http://markwatson.com\n³https://leanpub.com/lovinglisp\n⁴https://leanpub.com/u/markwatson\n⁵https://github.com/mark-watson?tab=repositories&q=&type=public\n⁶https://mastodon.social/@mark_watson\n⁷https://twitter.com/mark_l_watson\n\nPreface\nNotes on the Eighth Edition Published August 2022\nThe main change is splitting the Knowledge Graph Navigator (KGN) chapter that features the\nLispWorks CAPI UI APIs into three chapters for a library for KGN functionality, a text based\n(console) UI, and a CAPI based UI. I added examples using the OpenAI GPT-3 APIs. There are\nother small corrections and improvements.\nNotes on the Seventh Edition Published March 2021\nI added two short chapters to the previous edition: Knowledge Graph Sampler for Creating Small\nCustom Knowledge Graphs and Using Common Lisp With Wolfram/One.\nNotes on the Sixth Edition Published June 2020\nTwo examples optionally use the CAPI user interface toolkit provided with LispWorks Common\nLisp⁸ and work with the free personal edition. The first CAPI application is Knowledge Graph\nNavigator⁹ and the second CAPI example is Knowledge Graph Creator¹⁰. Both of these examples\nbuild up utilities for working with Knowledge Graphs and the Semantic Web.\nI expand the Plot Library chapter to generate either PNG graphics files or if you are using the free\npersonal edition of LispWorks you can also direct plotting output to a new window in interactive\nprograms.\nI added a new chapter on using the py4cl library to embed Python libraries and application code into\na Common Lisp system. I provide new examples for embedding spaCy and TensorFlow applications\nin Common Lisp applications. In earlier editions, I used a web services interface to wrap Python\ncode using spaCy and TensorFlow. I am leaving that chapter intact, renaming it from “Using Python\nDeep Learning Models In Common Lisp” to “Using Python Deep Learning Models In Common Lisp\nWith a Web Services Interface.” The new chapter for this edition is “Using the PY4CL Library to\nEmbed Python in Common Lisp.”\n⁸https://lispworks.com\n⁹http://knowledgegraphnavigator.com\n¹⁰http://kgcreator.com\n\nPreface\n3\nNotes on the Fifth Edition Published September 2019\nThere were two chapters added:\n• A complete application for processing text to generate data for Knowledge Graphs (targeting\nthe open source Neo4J graph database and also support RDF semantic web/linked data).\n• A library for accessing the state of the art spaCy natural language processing (NLP) library\nand also a state of the art deep learning model. These models are implemented in thin Python\nwrappers that use Python libraries like spaCy, PyTorch, and TensorFlow. These examples\nreplace a simple hybrid Java and Common Lisp example in previous editions.\nI have added text and explanations as appropriate throughout the book and I removed the CouchDB\nexamples.\nI have made large changes to how the code for this book is packaged. I have reorganized the example\ncode on GitHub by providing the examples as multiple Quicklisp libraries or applications. I now do\nthis with all of my Common Lisp code and it makes it easier to write smaller libraries that can be\ncomposed into larger applications. In my own workflow, I also like to use Makefile targets to build\nstandalone applications that can be run on other computers without installing Lisp development\nenvironments. Please follow the directions at the end of the Preface for configuring Quicklisp for\neasy builds and use of the example software for this book.\nWhy Use Common Lisp?\nWhy Common Lisp? Isn’t Common Lisp an old language? Do many people still use Common Lisp?\nI believe that using Lisp languages like Common Lisp, Clojure, Racket, and Scheme are all secret\nweapons useful in agile software development. An interactive development process and live\nproduction updates feel like a breath of fresh air if you have development on heavy weight like\nJava Enterprise Edition (JEE).\nYes, Common Lisp is an old language but with age comes stability and extremely good compiler\ntechnology. There is also a little inconsistency between different Common Lisp systems in such\nthings as handling threads but with a little up front knowledge you can choose which Common Lisp\nsystems will support your requirements.\nA Request from the Author\nI spent time writing this book to help you, dear reader. I release this book under the Creative\nCommons License and set the minimum purchase price to $0.00 (free!) in order to reach the most\nreaders. Under this license you can share a PDF version of this book with your friends and coworkers\n\nPreface\n4\nand I encourage you to do so. If you found this book on the web (or it was given to you) and if it\nprovides value to you then please consider doing one of the following to support my future writing\nefforts and also to support future updates to this book:\n• Purchase a copy of this book leanpub.com/lovinglisp/¹¹ or any other of my leanpub books at\nhttps://leanpub.com/u/markwatson¹²\n• Hire me as a consultant¹³\nI enjoy writing and your support helps me write new editions and updates for my books and to\ndevelop new book projects. Thank you!\nOlder Book Editions\nThe fourth edition of this book was released in May 2017 and the major changes were:\n• Added an example application KGCreator that processes text data to automatically generate\ndata for Knowledge Graphs. This example application supports the Neo4J graph database as\nwell as semantic web/linked data systems. The major changes were:\n• Added a backpropagation neural network example\n• Added a deep learning example using the Java based Armed Bear Common Lisp with the\npopular DeepLearning4j library\n• Added a heuristic search example\n• Added two machine learning examples (K-Means clustering and SVM classification) using the\nCLML library\n• A few edits to the previous text\nThe third edition was released in October 2014. The major changes made in the 2014 edition are:\n• I reworked the chapter Common Lisp Basics.\n• I added material to the chapter on using QuickLisp.\nThe second edition was released in 2013 and was derived from the version that I distributed on my\nweb site and I moved production of the book to leanpub.com¹⁴.\n¹¹https://leanpub.com/lovinglisp/\n¹²https://leanpub.com/u/markwatson\n¹³https://markwatson.com/\n¹⁴https://leanpub.com/u/markwatson\n\nPreface\n5\nAcknowledgments\nI would like to thank Jans Aasman¹⁵ for contributing as technical editor for the fourth edition of this\nbook. Jans is CEO of Franz.com¹⁶ which sells Allegro Common Lisp¹⁷ as well as tools for semantic\nweb and linked data applications.\nI would like to thank the following people who made suggestions for improving previous editions\nof this book:\nSam Steingold, Andrew Philpot, Kenny Tilton, Mathew Villeneuve, Eli Draluk, Erik Winkels, Adam\nShimali, and Paolo Amoroso.\nI would like to also thank several people who pointed out typo errors in this book and for specific\nsuggestions: Martin Lightheart, Tong-Kiat Tan, Rainer Joswig, Gerold Rupprecht, HN member\nrurban, David Cortesi. I would like to thank the following Reddit /r/lisp readers who pointed out\nmistakes in the fifth edition of this book: arnulfslayer, rpiirp, and itmuckel. I would like to thank\nTed Briscoe for pointing out a problem with the spacy web client example in the 6th edition.\nI would like to thank Paul Graham for coining the phrase “The Secret Weapon” (in his excellent\npaper “Beating the Averages”) in discussing the advantages of Lisp and giving me permission to\nreuse his phrase.\nI would especially like to thank my wife Carol Watson for her fine work in\nediting this book.\nSetting Up Your Common Lisp Development System\nand Quicklisp\nThese instructions assume the use of SBCL. See comments for LispWorks, Franz Common Lisp,\nand Closure Common List at the end of this section. I assume that you have installed SBCL and\nQuicklisp by following the instructions at lisp-lang.org/learn/getting-started¹⁸. These instructions\nalso guide you through installing the Slime extensions for Emacs. I use both Emacs + Slime and\nVSCode with Common Lisp plugins for editing Common Lisp. If you like VSCode then I recommend\nYasuhiro Matsumoto’s Lisp plugin for syntax highlighting. For both Emacs and VSCode I usually\nrun a separate REPL in a terminal window and don’t run an editor-integrated REPL. I think that I\nam in the minority in using a separate REPL running in a shell.\nI have been using Common Lisp since about 1982 and Quicklisp (developed and maintained by Zach\nBeane¹⁹) has been the most revolutionary change in my Common Lisp development (even more so\nthan getting a hardware Lisp Machine and the availability of Coral Common Lisp on the Macintosh).\n¹⁵https://en.wikipedia.org/wiki/Jans_Aasman\n¹⁶http://franz.com/\n¹⁷http://franz.com/products/allegro-common-lisp/\n¹⁸https://lisp-lang.org/learn/getting-started/\n¹⁹https://www.xach.com\n\nPreface\n6\nYou can follow the directions on the main GitHub repository for this book: https://github.com/mark-\nwatson/loving-common-lisp²⁰ to get the examples set up to run on your computer. Starting with the\n8th edition, I have a new scheme for distributing the book examples on GitHub:\n• A few short example Common Lisp code snippets are still kept in the main repository for the\nbook: https://github.com/mark-watson/loving-common-lisp²¹.\n• The longer examples are now stored in separate GitHub repositories to facilitate using them as\nreusable Quicklisp libraries.\n• Clone the main GitHub repository and copy the Makefile²² to the directory ∼/quicklisp/local-\nprojects/ on your computer.\n• Change directory to ∼/quicklisp/local-projects/ and run the Makefile target make fetch to\ncopy all separate GitHub repositories to subdirectories of ∼/quicklisp/local-projects/.\n• You can now load any book example using Quicklisp, for example: (ql:quickload :sparql).\nFor example, the subdirectory loving-common-lisp/src/spacy-py4cl contains a package named\nspacy-py4cl that can now be accessed from any directory on your system using:\n1\n$ sbcl\n2\n(ql:quickload \"spacy-py4cl\")\n3\n* (spacy-py4cl:nlp \"My sister has a dog Henry. She loves him.\")\n4\n* (defvar x (spacy-py4cl:nlp \"President Bill Clinton went to Congress. He gave a spe\\\n5\nech on taxes and Mexico.\"))\nThis example uses the deep learning NLP models in spaCy which is a Python library - see the chapter\non NLP for details on installing the Python dependencies. Note that only a few examples in this book\nrequire Python dependencies.\nI have used the SBCL implementation of Common Lisp in this book. There are many fine Common\nLisp implementations from Franz, LispWorks, Clozure Common Lisp, etc. I usually use LispWorks for\nmy professional development work. If you have any great difficulty adopting the examples to your\nchoice of Common Lisp implementations and performing web search does not suggest a solution\nthen you can reach me through my web site, markwatson.com²³.\n²⁰https://github.com/mark-watson/loving-common-lisp\n²¹https://github.com/mark-watson/loving-common-lisp\n²²https://raw.githubusercontent.com/mark-watson/loving-common-lisp/master/Makefile\n²³https://markwatson.com\n\nIntroduction\nThis book is intended to get you, the reader, programming quickly in Common Lisp. Although the\nLisp programming language is often associated with artificial intelligence, this introduction is on\ngeneral Common Lisp programming techniques. Later we will look at general example applications\nand artificial intelligence examples.\nThe Common Lisp program examples are distributed on the github repo for this book²⁴.\nWhy Did I Write this Book?\nWhy the title “Loving Common Lisp”? Simple! I have been using Lisp for almost 40 years and seldom\ndo I find a better match between a programming language and the programming job at hand. I am\nnot a total fanatic on Lisp, however. I often use Python for deep learning. I like Ruby, Java and\nJavascript for server side programming, and the few years that I spent working on Nintendo video\ngames and virtual reality systems for SAIC and Disney, I found C++ to be a good bet because of\nstringent runtime performance requirements. For some jobs, I find the logic-programming paradigm\nuseful: I also enjoy the Prolog language.\nIn any case, I love programming in Lisp, especially the industry standard Common Lisp. As I\nwrote the second edition of this book over a decade ago, I had been using Common Lisp almost\nexclusively for an artificial intelligence project for a health care company and for commercial\nproduct development. While working on the third edition of this book, I was not using Common\nLisp professionally but since the release of the Quicklisp Common Lisp package manager I have\nfound myself enjoying using Common Lisp more for small side projects. I use Quicklisp throughout\nin the third edition example code so you can easily install required libraries. For the fourth and fifth\neditions of this book I have added more examples using neural networks and deep learning. In this\nnew sixth edition I have added a complete application that uses CAP for the user interface.\nAs programmers, we all (hopefully) enjoy applying our experience and brains for tackling interesting\nproblems. My wife and I recently watched a two-night 7-hour PBS special “Joseph Campbell, and the\nPower of Myths.” Campbell, a college professor for almost 40 years, said that he always advised his\nstudents to “follow their bliss” and not to settle for jobs and avocations that are not what they truly\nwant to do. That said I always feel that when a job calls for using Java, Python or other languages\nbesides Lisp, that even though I may get a lot of pleasure from the job I am not following my bliss.\nMy goal in this book is to introduce you to one of my favorite programming languages, Common\nLisp. I assume that you already know how to program in another language but if you are a complete\nbeginner you can still master the material in this book with some effort. I challenge you to make\nthis effort.\n²⁴https://github.com/mark-watson/loving-common-lisp\n\nIntroduction\n8\nFree Software Tools for Common Lisp Programming\nThere are several Common Lisp compilers and runtime tools available for free on the web:\n• CLISP – licensed under the GNU GPL and is available for Windows, Macintosh, and Linux/U-\nnix\n• Clozure Common Lisp (CCL) – open source with good Mac OS X and Linux support\n• CMU Common Lisp – open source implementation\n• SBCL – derived from CMU Common Lisp\n• ECL – compiles using a separate C/C++ compiler\n• ABCL – Armed Bear Common Lisp for the JVM\nThere are also fine commercial Common Lisp products:\n• LispWorks – high quality and reasonably priced system for Windows and Linux. No charge\nfor distributing compiled applications lispworks.com²⁵\n• Allegro Common Lisp - high quality, great support and higher cost. franz.com²⁶\n• MCL – Macintosh Common Lisp. I used this Lisp environment in the late 1980s. MCL was so\ngood that I gave away my Xerox 1108 Lisp Machine and switched to a Mac and MCL for my\ndevelopment work. Now open source but only runs on the old MacOS\nI currently (mostly) use SBCL, CCL, and LispWorks. The SBCL compiler produces very fast code\nand the compiler warning can be of great value in finding potential problems with your code. Like\nCCL because it compiles quickly so is often preferable for development.\nFor working through this book, I will assume that you are using SBCL or CCL. For the example in\nthe last chapter you will need LispWorks and the free Personal edition is fine for the purposes of\nexperimenting with the example application and the CAPI user interface library.\nMaking Book Examples Run Portably on Most Common\nLisp Implementations\nMany of the book examples require making web service calls. In general when I am writing Common\nLisp applications that require making REST calls I prefer using 3rd party Common Lisp libraries like\nDrakma or Dexador. However it is sometimes a little tricky to set up Common Lisp on different\noperating systems and CPU architectures with libopenssl, libcrypto, etc. Because of this, in book\nexamples I run the external curl program using uiop:run-program and collect the output as a string\nthat is then parsed as JSON or CSV data. The overhead of starting an external process is very small\n²⁵http://www.lispworks.com\n²⁶http://franz.com\n\nIntroduction\n9\ncompared to calling a web service so in your own applications you can either follow my example of\nusing curl or use the Drakma or Dexador libraries. Using the Apple M1 processor on macOS can be\nparticularly problematic with OpenSSL issues.\nI also use the excellent Common Lisp to Python bridge library py4cl in a few book examples. Usually\npy4cl installs without problems.\nHow is Lisp Different from Languages like Java and\nC++?\nThis is a trick question! Lisp is slightly more similar to Java than C++ because of automated memory\nmanagement so we will start by comparing Lisp and Java.\nIn Java, variables are strongly typed while in Common Lisp values are strongly typed. For example,\nconsider the Java code:\n1\nFloat x = new Float(3.14f);\n2\nString s = \"the cat ran\" ;\n3\nObject any_object = null;\n4\nany_object = s;\n5\nx = s;\n// illegal: generates a\n6\n// compilation error\nHere, in Java, variables are strongly typed so a variable x of type Float can’t legally be assigned a\nstring value: the code in line 5 would generate a compilation error. Lisp code can assign a value to\na variable and then reassign another value of a different type.\nJava and Lisp both provide automatic memory management. In either language, you can create new\ndata structures and not worry about freeing memory when the data is no longer used, or to be more\nprecise, is no longer referenced.\nCommon Lisp is an ANSI standard language. Portability between different Common Lisp implemen-\ntations and on different platforms is very good. I have used Clozure Common Lisp, SBCL, Allegro\nLisp (from Franz Inc), LispWorks, and CLISP that all run well on Windows, Mac OS X, and Linux.\nAs a Common Lisp developer you will have great flexibility in tools and platforms.\nANSI Common Lisp was the first object oriented language to become an ANSI standard language.\nThe Common Lisp Object System (CLOS) is probably the best platform for object oriented\nprogramming.\nIn C++ programs, a common bug that affects a program’s efficiency is forgetting to free memory that\nis no longer used. In a virtual memory system, the effect of a program’s increasing memory usage\nis usually just poorer system performance but can lead to system crashes or failures if all available\nvirtual memory is exhausted. A worse type of C++ error is to free memory and then try to use it.\nCan you say “program crash”? C programs suffer from the same types of memory related errors.\n\nIntroduction\n10\nSince computer processing power is usually much less expensive than the costs of software\ndevelopment, it is almost always worth while to give up a few percent of runtime efficiency and let\nthe programming environment of runtime libraries manage memory for you. Languages like Lisp,\nRuby, Python, and Java are said to perform automatic garbage collection.\nI have written six books on Java, and I have been quoted as saying that for me, programming in Java\nis about twice as efficient (in terms of my time) as programming in C++. I base this statement on\napproximately ten years of C++ experience on projects for SAIC, PacBell, Angel Studios, Nintendo,\nand Disney. I find Common Lisp and other Lisp languages like Clojure and Scheme to be about twice\nas efficient (again, in terms of my time) as Java. That is correct: I am claiming a four times increase\nin my programming productivity when using Common Lisp vs. C++.\nWhat do I mean by programming productivity? Simple: for a given job, how long does it take me to\ndesign, code, debug, and later maintain the software for a given task.\nAdvantages of Working in a Lisp Environment\nWe will soon see that Lisp is not just a language; it is also a programming environment and runtime\nenvironment.\nThe beginning of this book introduces the basics of Lisp programming. In later chapters, we will\ndevelop interesting and non-trivial programs in Common Lisp that I argue would be more difficult\nto implement in other languages and programming environments.\nThe big win in programming in a Lisp environment is that you can set up an environment and\ninteractively write new code and test new code in small pieces. We will cover programming with\nlarge amounts of data in the Chapter on Natural Language Processing, but let me share a a general\nuse case for work that I do that is far more efficient in Lisp:\nMuch of my Lisp programming used to be writing commercial natural language processing (NLP)\nprograms for my company www.knowledgebooks.com. My Lisp NLP code uses a large amount of\nmemory resident data; for example: hash tables for different types of words, hash tables for text\ncategorization, 200,000 proper nouns for place names (cities, counties, rivers, etc.), and about 40,000\ncommon first and last names of various nationalities.\nIf I was writing my NLP products in C++, I would probably use a relational database to store this\ndata because if I read all of this data into memory for each test run of a C++ program, I would\nwait 30 seconds every time that I ran a program test. When I start working in any Common\nLisp environment, I do have to load the linguistic data into memory one time, but then can\ncode/test/code/test… for hours with no startup overhead for reloading the data that my programs\nneed to run. Because of the interactive nature of Lisp development, I can test small bits of code when\ntracking down errors and when writing new code.\nIt is a personal preference, but I find the combination of the stable Common Lisp language and an\niterative Lisp programming environment to be much more productive than other languages and\nprogramming environments.\n\nCommon Lisp Basics\nThe material in this chapter will serve as an introduction to Common Lisp. I have attempted to\nmake this book a self contained resource for learning Common Lisp and to provide code examples\nto perform common tasks. If you already know Common Lisp and bought this book for the code\nexamples later in this book then you can probably skip this chapter.\nFor working through this chapter we will be using the interactive shell, or repl, built into SBCL and\nother Common Lisp systems. For this chapter it is sufficient for you to download and install SBCL²⁷.\nPlease install SBCL right now, if you have not already done so.\nGetting Started with SBCL\nWhen we start SBCL, we see an introductory message and then an input prompt. We will start with\na short tutorial, walking you through a session using SBCL repl (other Common LISP systems are\nvery similar). A repl is an interactive console where you type expressions and see the results of\nevaluating these expressions. An expression can be a large block of code pasted into the repl, using\nthe load function to load Lisp code into the repl, calling functions to test them, etc. Assuming that\nSBCL is installed on your system, start SBCL by running the SBCL program:\n1\n% sbcl\n2\n(running SBCL from: /Users/markw/sbcl)\n3\nThis is SBCL 2.0.2, an implementation of ANSI Common Lisp.\n4\nMore information about SBCL is available at <http://www.sbcl.org/>.\n5\n6\nSBCL is free software, provided as is, with absolutely no warranty.\n7\nIt is mostly in the public domain; some portions are provided under\n8\nBSD-style licenses.\nSee the CREDITS and COPYING files in the\n9\ndistribution for more information.\n10\n11\n* (defvar x 1.0)\n12\n13\nX\n14\n* x\n15\n16\n1.0\n17\n* (+ x 1)\n²⁷http://www.sbcl.org/platform-table.html\n\nCommon Lisp Basics\n12\n18\n19\n2.0\n20\n* x\n21\n22\n1.0\n23\n* (setq x (+ x 1))\n24\n25\n2.0\n26\n* x\n27\n28\n2.0\n29\n* (setq x \"the dog chased the cat\")\n30\n31\n\"the dog chased the cat\"\n32\n* x\n33\n34\n\"the dog chased the cat\"\n35\n* (quit)\nWe started by defining a new variable x in line 11. Notice how the value of the defvar macro is the\nsymbol that is defined. The Lisp reader prints X capitalized because symbols are made upper case\n(we will look at the exception later).\nIn Lisp, a variable can reference any data type. We start by assigning a floating point value to the\nvariable x, using the + function to add 1 to x in line 17, using the setq function to change the value\nof x in lines 23 and 29 first to another floating point value and finally setting x to a string value.\nOne thing that you will have noticed: function names always occur first, then the arguments to a\nfunction. Also, parenthesis is used to separate expressions.\nI learned to program Lisp in 1976 and my professor half-jokingly told us that Lisp was an acronym\nfor “Lots-of Irritating Superfluous Parenthesis.” There may be some truth in this when you are just\nstarting with Lisp programming, but you will quickly get used to the parenthesis, especially if you\nuse an editor like Emacs that automatically indents Lisp code for you and highlights the opening\nparenthesis for every closing parenthesis that you type. Many other editors support coding in Lisp\nbut I personally use Emacs or sometimes VScode (with Common Lisp plugins) to edit Lisp code.\nBefore you proceed to the next chapter, please take the time to install SBCL on your computer and\ntry typing some expressions into the Lisp listener. If you get errors, or want to quit, try using the\nquit function:\n\nCommon Lisp Basics\n13\n1\n* (+ 1 2 3 4)\n2\n3\n10\n4\n* (quit)\n5\nBye.\nIf you get an error you can enter help to get options for handling an error. When I get an error and\nhave a good idea of what caused the error then I just enter :a: to abort out of the error).\nAs we discussed in the introduction, there are many different Lisp programming environments that\nyou can choose from. I recommend a free set of tools: Emacs, Quicklisp, slime, and SBCL. Emacs is\na fine text editor that is extensible to work well with many programming languages and document\ntypes (e.g., HTML and XML). Slime is an Emacs extension package that greatly facilitates Lisp\ndevelopment. SBCL is a robust Common Lisp compiler and runtime system that is often used in\nproduction.\nWe will cover the Quicklisp package manager and using Quicklisp to setup Slime and Emacs in a\nlater chapter.\nI will not spend much time covering the use of Emacs as a text editor in this book since you can try\nmost of the example code snippets in the book text by copying and then pasting them into a SBCL\nrepl and by loading the book example source files directly into a repl. If you already use Emacs then\nI recommend that you do set up Slime sooner rather than later and start using it for development. If\nyou are not already an Emacs user and do not mind spending the effort to learn Emacs, then search\nthe web first for an Emacs tutorial. That said, you will easily be able to use the example code from\nthis book using any text editor you like with a SBCL repl. I don’t use the vi or vim editors but if vi\nis your weapon of choice for editing text then a web search for “common lisp vi vim repl” should\nget you going for developing Common Lisp code with vi or vim. If you are not already an Emacs or\nvi user then using VSCode with a Common Lisp plugin is recommended.\nHere, we will assume that under Windows, Unix, Linux, or Mac OS X you will use one command\nwindow to run SBCL and a separate editor that can edit plain text files.\nMaking the repl Nicer using rlwrap\nWhile reading the last section you (hopefully!) played with the SBCL interactive repl. If you haven’t\nplayed with the repl, I won’t get too judgmental except to say that if you do not play with the\nexamples as you read you will not get the full benefit from this book.\nDid you notice that the backspace key does not work in the SBCL repl? The way to fix this is to install\nthe GNU rlwrap utility. On OS X, assuming that you have homebrew²⁸ installed, install rlwrap with:\n²⁸http://mxcl.github.io/homebrew/\n\nCommon Lisp Basics\n14\n1\nbrew install rlwrap\nIf you are running Ubuntu Linux, install rlwrap using:\n1\nsudo apt-get install rlwrap\nYou can then create an alias for bash or zsh using something like the following to define a command\nrsbcl:\n1\nalias rsbcl='rlwrap sbcl'\nThis is fine, just remember to run sbcl if you don’t need rlwrap command line editing or run rsbcl\nwhen you do need command line editing. That said, I find that I always want to run SBCL with\ncommand line editing, so I redefine sbcl on my computers using:\n1\n->\n~\nwhich sbcl\n2\n/Users/markw/sbcl/sbcl\n3\n->\n~\nalias sbcl='rlwrap /Users/markw/sbcl/sbcl'\nThis alias is different on my laptops and servers, since I don’t usually install SBCL in the default\ninstallation directory. For each of my computers, I add an appropriate alias in my .zshrc file (if I am\nrunning zsh) or my .bashrc file (if I am running bash).\nThe Basics of Lisp Programming\nAlthough we will use SBCL in this book, any Common Lisp environment will do fine. In previous\nsections, we saw the top-level Lisp prompt and how we could type any expression that would be\nevaluated:\n1\n* 1\n2\n1\n3\n* 3.14159\n4\n3.14159\n5\n* \"the dog bit the cat\"\n6\n\"the dog bit the cat\"\n7\n* (defun my-add-one (x)\n8\n(+ x 1))\n9\nMY-ADD-ONE\n10\n* (my-add-one -10)\n11\n-9\n\nCommon Lisp Basics\n15\nNotice that when we defined the function my-add-one in lines 7 and 8, we split the definition over\ntwo lines and on line 8 you don’t see the “*” prompt from SBCL – this lets you know that you have\nnot yet entered a complete expression. The top level Lisp evaluator counts parentheses and considers\na form to be complete when the number of closing parentheses equals the number of opening\nparentheses and an expression is complete when the parentheses match. I tend to count in my head,\nadding one for every opening parentheses and subtracting one for every closing parentheses – when\nI get back down to zero then the expression is complete. When we evaluate a number (or a variable),\nthere are no parentheses, so evaluation proceeds when we hit a new line (or carriage return).\nThe Lisp reader by default tries to evaluate any form that you enter. There is a reader macro ‘ that\nprevents the evaluation of an expression. You can either use the ‘ character or quote:\n1\n* (+ 1 2)\n2\n3\n3\n* '(+ 1 2)\n4\n(+ 1 2)\n5\n* (quote (+ 1 2))\n6\n(+ 1 2)\n7\n*\nLisp supports both global and local variables. Global variables can be declared using defvar:\n1\n* (defvar *x* \"cat\")\n2\n*X*\n3\n* *x*\n4\n\"cat\"\n5\n* (setq *x* \"dog\")\n6\n\"dog\"\n7\n* *x*\n8\n\"dog\"\n9\n* (setq *x* 3.14159)\n10\n3.14159\n11\n* *x*\n12\n3.14159\nOne thing to be careful of when defining global variables with defvar: the declared global variable\nis dynamically scoped. We will discuss dynamic versus lexical scoping later, but for now a warning:\nif you define a global variable avoid redefining the same variable name inside functions. Lisp\nprogrammers usually use a global variable naming convention of beginning and ending dynamically\nscoped global variables with the * character. If you follow this naming convention and also do not\nuse the * character in local variable names, you will stay out of trouble. For convenience, I do not\nalways follow this convention in short examples in this book.\n\nCommon Lisp Basics\n16\nLisp variables have no type. Rather, values assigned to variables have a type. In this last example, the\nvariable x was set to a string, then to a floating-point number. Lisp types support inheritance and\ncan be thought of as a hierarchical tree with the type t at the top. (Actually, the type hierarchy is a\nDAG, but we can ignore that for now.) Common Lisp also has powerful object oriented programming\nfacilities in the Common Lisp Object System (CLOS) that we will discuss in a later chapter.\nHere is a partial list of types (note that indentation denotes being a subtype of the preceding type):\n1\nt\n[top level type (all other types are a sub-type)]\n2\nsequence\n3\nlist\n4\narray\n5\nvector\n6\nstring\n7\nnumber\n8\nfloat\n9\nrational\n10\ninteger\n11\nratio\n12\ncomplex\n13\ncharacter\n14\nsymbol\n15\nstructure\n16\nfunction\n17\nhash-table\nWe can use the typep function to test the type of value of any variable or expression or use type-of\nto get type information of any value):\n1\n* (setq x '(1 2 3))\n2\n(1 2 3)\n3\n* (typep x 'list)\n4\nT\n5\n* (typep x 'sequence)\n6\nT\n7\n* (typep x 'number)\n8\nNIL\n9\n* (typep (+ 1 2 3) 'number)\n10\nT\n11\n* (type-of 3.14159)\n12\nsingle-float\n13\n* (type-of \"the dog ran quickly\")\n14\n(simple-array character (19))\n\nCommon Lisp Basics\n17\n15\n* (type-of 100193)\n16\n(integer 0 4611686018427387903)\nA useful feature of all ANSI standard Common Lisp implementations’ top-level listener is that it sets\n* to the value of the last expression evaluated. For example:\n1\n* (+ 1 2 3 4 5)\n2\n15\n3\n* *\n4\n15\n5\n* (setq x *)\n6\n15\n7\n* x\n8\n15\nAll Common Lisp environments set * to the value of the last expression evaluated. This example\nmay be slightly confusing because * is also the prompt character in the SBCL repl that indicates that\nyou can enter a new expression for evaluation. For example in line 3, the first * character is the repl\nprompt and the second * we type in to see that value of the previous expression that we typed into\nthe repl.\nFrequently, when you are interactively testing new code, you will call a function that you just wrote\nwith test arguments; it is useful to save intermediate results for later testing. It is the ability to create\ncomplex data structures and then experiment with code that uses or changes these data structures\nthat makes Lisp programming environments so effective.\nCommon Lisp is a lexically scoped language that means that variable declarations and function\ndefinitions can be nested and that the same variable names can be used in nested let forms; when\na variable is used, the current let form is searched for a definition of that variable and if it is not\nfound, then the next outer let form is searched. Of course, this search for the correct declaration\nof a variable is done at compile time so there need not be extra runtime overhead. We can nest\ndefun special form inside each other and inside let expressions but this defines the nested functions\nglobally. We use the special forms flet and labels to define functions inside a scoped environment.\nFunctions defined inside a labels special form can be recursive while functions defined inside a flet\nspecial form cannot be recursive. Consider the following example in the file nested.lisp (all example\nfiles are in the src directory):\n\nCommon Lisp Basics\n18\n1\n(flet ((add-one (x)\n2\n(+ x 1))\n3\n(add-two (x)\n4\n(+ x 2)))\n5\n(format t \"redefined variables: ~A\n~A~%\" (add-one 100) (add-two 100)))\n6\n7\n(let ((a 3.14))\n8\n(defun test2 (x)\n9\n(print x))\n10\n(test2 a))\n11\n12\n(test2 50)\n13\n14\n(let ((x 1)\n15\n(y 2))\n16\n;; define a test function nested inside a let statement:\n17\n(flet ((test (a b)\n18\n(let ((z (+ a b)))\n19\n;; define a helper function nested inside a let/function/let:\n20\n(flet ((nested-function (a)\n21\n(+ a a)))\n22\n(nested-function z)))))\n23\n;; call nested function 'test':\n24\n(format t \"test result is ~A~%\" (test x y))))\n25\n26\n(let ((z 10))\n27\n(labels ((test-recursion (a)\n28\n(format t \"test-recursion ~A~%\" (+ a z))\n29\n(if (> a 0)\n30\n(test-recursion (- a 1)))))\n31\n(test-recursion 5)))\nWe define a top level flet special form in lines 1-5 that defines two nested functions add-one and\nadd-two and then calls each nested function in the body of the flet special form. For many years I\nhave used nested defun special forms inside let expressions for defining local functions and you will\nnotice this use in a few later examples. However, functions defined inside defun special forms have\nglobal visibility so they are not hidden in the local context where they are defined. The example\nof a nested defun in lines 7-12 shows that the function test2 has global visibility inside the current\npackage.\nFunctions defined inside of a flet special form have access to variables defined in the outer scope\ncontaining the flet (also applies to labels). We see this in lines 14-24 where the local variables x and\ny defined in the let expression are visible inside the function nested-function defined inside the\nflet.\n\nCommon Lisp Basics\n19\nThe final example in lines 26-31 shows a recursive function defined inside a labels special form.\nAssuming that we started SBCL in the src directory we can then use the Lisp load function to\nevaluate the contents of the file nested.lisp in the sub-directory code_snippets_for_book using\nthe load function:\n* (load \"./code_snippets_for_book/nested.lisp\")\nredefined variables: 101\n102\n3.14\n50 test result is 6\ntest-recursion 15\ntest-recursion 14\ntest-recursion 13\ntest-recursion 12\ntest-recursion 11\ntest-recursion 10\nT\n*\nThe function load returned a value of t (prints in upper case as T) after successfully loading the file.\nWe will use Common Lisp vectors and arrays frequently in later chapters, but will also briefly\nintroduce them here. A singly dimensioned array is also called a vector. Although there are often\nmore efficient functions for handling vectors, we will just look at generic functions that handle any\ntype of array, including vectors. Common Lisp provides support for functions with the same name\nthat take different argument types; we will discuss this in some detail when we cover this in the\nlater chapter on CLOS. We will start by defining three vectors v1, v2, and v3:\n1\n* (setq v1 (make-array '(3)))\n2\n#(NIL NIL NIL)\n3\n* (setq v2 (make-array '(4) :initial-element \"lisp is good\"))\n4\n#(\"lisp is good\" \"lisp is good\" \"lisp is good\" \"lisp is good\")\n5\n* (setq v3 #(1 2 3 4 \"cat\" '(99 100)))\n6\n#(1 2 3 4 \"cat\" '(99 100))\nIn line 1, we are defining a one-dimensional array, or vector, with three elements. In line 3 we specify\nthe default value assigned to each element of the array v2. In line 5 I use the form for specifying\narray literals using the special character #. The function aref can be used to access any element in\nan array:\n\nCommon Lisp Basics\n20\n* (aref v3 3)\n4\n* (aref v3 5)\n'(99 100)\n*\nNotice how indexing of arrays is zero-based; that is, indices start at zero for the first element of a\nsequence. Also notice that array elements can be any Lisp data type. So far, we have used the special\noperator setq to set the value of a variable. Common Lisp has a generalized version of setq called\nsetf that can set any value in a list, array, hash table, etc. You can use setf instead of setq in all\ncases, but not vice-versa. Here is a simple example:\n* v1\n#(NIL NIL NIL)\n* (setf (aref v1 1) \"this is a test\")\n\"this is a test\"\n* v1\n#(NIL \"this is a test\" NIL)\n*\nWhen writing new code or doing quick programming experiments, it is often easiest (i.e., quickest to\nprogram) to use lists to build interesting data structures. However, as programs mature, it is common\nto modify them to use more efficient (at runtime) data structures like arrays and hash tables.\nSymbols\nWe will discuss symbols in more detail the Chapter on Common Lisp Packages. For now, it is enough\nfor you to understand that symbols can be names that refer to variables. For example:\n> (defvar *cat* \"bowser\")\n*CAT*\n* *cat*\n\"bowser\"\n* (defvar *l* (list *cat*))\n*L*\n* *l*\n(\"bowser\")\n*\nNote that the first defvar returns the defined symbol as its value. Symbols are almost always\nconverted to upper case. An exception to this “upper case rule” is when we define symbols that\nmay contain white space using vertical bar characters:\n\nCommon Lisp Basics\n21\n* (defvar |a symbol with Space Characters| 3.14159)\n|a symbol with Space Characters|\n* |a symbol with Space Characters|\n3.14159\n*\nOperations on Lists\nLists are a fundamental data structure of Common Lisp. In this section, we will look at some of the\nmore commonly used functions that operate on lists. All of the functions described in this section\nhave something in common: they do not modify their arguments.\nIn Lisp, a cons cell is a data structure containing two pointers. Usually, the first pointer in a cons cell\nwill point to the first element in a list and the second pointer will point to another cons representing\nthe start of the rest of the original list.\nThe function cons takes two arguments that it stores in the two pointers of a new cons data structure.\nFor example:\n* (cons 1 2)\n(1 . 2)\n* (cons 1 '(2 3 4))\n(1 2 3 4)\n*\nThe first form evaluates to a cons data structure while the second evaluates to a cons data structure\nthat is also a proper list. The difference is that in the second case the second pointer of the freshly\ncreated cons data structure points to another cons cell.\nFirst, we will declare two global variables l1 and l2 that we will use in our examples. The list l1\ncontains five elements and the list l2 contains four elements:\n* (defvar l1 '(1 2 (3) 4 (5 6)))\nL1\n* (length l1)\n5\n* (defvar l2 '(the \"dog\" calculated 3.14159))\nL2\n* l1\n(1 2 (3) 4 (5 6))\n* l2\n(THE \"dog\" CALCULATED 3.14159)\n>\n\nCommon Lisp Basics\n22\nYou can also use the function list to create a new list; the arguments passed to function list are the\nelements of the created list:\n* (list 1 2 3 'cat \"dog\")\n(1 2 3 CAT \"dog\")\n*\nThe function car returns the first element of a list and the function cdr returns a list with its first\nelement removed (but does not modify its argument):\n* (car l1)\n1\n* (cdr l1)\n(2 (3) 4 (5 6))\n*\nUsing combinations of car and cdr calls can be used to extract any element of a list:\n* (car (cdr l1))\n2\n* (cadr l1)\n2\n*\nNotice that we can combine calls to car and cdr into a single function call, in this case the function\ncadr. Common Lisp defines all functions of the form cXXr, cXXXr, and cXXXXr where X can be\neither a or d.\nSuppose that we want to extract the value 5 from the nested list l1. Some experimentation with using\ncombinations of car and cdr gets the job done:\n* l1\n(1 2 (3) 4 (5 6))\n* (cadr l1)\n2\n* (caddr l1)\n(3)\n(car (caddr l1))\n3\n* (caar (last l1))\n5\n* (caar (cddddr l1))\n5\n*\n\nCommon Lisp Basics\n23\nThe function last returns the last cdr of a list (i.e., the last element, in a list):\n* (last l1)\n((5 6))\n*\nCommon list supplies alternative functions to car and cdr that you might find more readable: first,\nsecond, third, fourth, and rest. Here are some examples:\n* (defvar *x* '(1 2 3 4 5))\n*X*\n* (first *x*)\n1\n* (rest *x*)\n(2 3 4 5)\n* (second *x*)\n2\n* (third *x*)\n3\n* (fourth *x*)\n4\nThe function nth takes two arguments: an index of a top-level list element and a list. The first index\nargument is zero based:\n* l1\n(1 2 (3) 4 (5 6))\n* (nth 0 l1)\n1\n* (nth 1 l1)\n2\n* (nth 2 l1)\n(3)\n*\nThe function cons adds an element to the beginning of a list and returns as its value a new list (it\ndoes not modify its arguments). An element added to the beginning of a list can be any Lisp data\ntype, including another list:\n\nCommon Lisp Basics\n24\n* (cons 'first l1)\n(FIRST 1 2 (3) 4 (5 6))\n* (cons '(1 2 3) '(11 22 33))\n((1 2 3) 11 22 33)\n*\nThe function append takes two lists as arguments and returns as its value the two lists appended\ntogether:\n* l1\n(1 2 (3) 4 (5 6))\n* l2\n('THE \"dog\" 'CALCULATED 3.14159)\n* (append l1 l2)\n(1 2 (3) 4 (5 6) THE \"dog\" CALCULATED 3.14159)\n* (append '(first) l1)\n(FIRST 1 2 (3) 4 (5 6))\n*\nA frequent error that beginning Lisp programmers make is not understanding shared structures in\nlists. Consider the following example where we generate a list y by reusing three copies of the list x:\n* (setq x '(0 0 0 0))\n(0 0 0 0)\n* (setq y (list x x x))\n((0 0 0 0) (0 0 0 0) (0 0 0 0))\n* (setf (nth 2 (nth 1 y)) 'x)\nX\n* x\n(0 0 X 0)\n* y\n((0 0 X 0) (0 0 X 0) (0 0 X 0))\n* (setq z '((0 0 0 0) (0 0 0 0) (0 0 0 0)))\n((0 0 0 0) (0 0 0 0) (0 0 0 0))\n* (setf (nth 2 (nth 1 z)) 'x)\nX\n* z\n((0 0 0 0) (0 0 X 0) (0 0 0 0))\n*\nWhen we change the shared structure referenced by the variable x that change is reflected three times\nin the list y. When we create the list stored in the variable z we are not using a shared structure.\n\nCommon Lisp Basics\n25\nUsing Arrays and Vectors\nUsing lists is easy but the time spent accessing a list element is proportional to the length of the list.\nArrays and vectors are more efficient at runtime than long lists because list elements are kept on\na linked-list that must be searched. Accessing any element of a short list is fast, but for sequences\nwith thousands of elements, it is faster to use vectors and arrays.\nBy default, elements of arrays and vectors can be any Lisp data type. There are options when creating\narrays to tell the Common Lisp compiler that a given array or vector will only contain a single data\ntype (e.g., floating point numbers) but we will not use these options in this book.\nVectors are a specialization of arrays; vectors are arrays that only have one dimension. For efficiency,\nthere are functions that only operate on vectors, but since array functions also work on vectors,\nwe will concentrate on arrays. In the next section, we will look at character strings that are a\nspecialization of vectors.\nWe could use the generalized make-sequence function to make a singularly dimensioned array (i.e.,\na vector). Restart sbcl and try:\n* (defvar x (make-sequence 'vector 5 :initial-element 0))\nX\n* x\n#(0 0 0 0 0)\n*\nIn this example, notice the print format for vectors that looks like a list with a proceeding # character.\nAs seen in the last section, we use the function make-array to create arrays:\n* (defvar y (make-array '(2 3) :initial-element 1))\nY\n* y\n#2A((1 1 1) (1 1 1))\n>\nNotice the print format of an array: it looks like a list proceeded by a # character and the integer\nnumber of dimensions.\nInstead of using make-sequence to create vectors, we can pass an integer as the first argument of\nmake-array instead of a list of dimension values. We can also create a vector by using the function\nvector and providing the vector contents as arguments:\n\nCommon Lisp Basics\n26\n* (make-array 10)\n#(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL)\n* (vector 1 2 3 'cat)\n#(1 2 3 CAT)\n*\nThe function aref is used to access sequence elements. The first argument is an array and the\nremaining argument(s) are array indices. For example:\n* x\n#(0 0 0 0 0)\n* (aref x 2)\n0\n* (setf (aref x 2) \"parrot\")\n\"parrot\"\n* x\n#(0 0 \"parrot\" 0 0)\n* (aref x 2)\n\"parrot\"\n* y\n#2A((1 1 1) (1 1 1))\n* (setf (aref y 1 2) 3.14159)\n3.14159\n* y\n#2A((1 1 1) (1 1 3.14159))\n*\nUsing Strings\nIt is likely that even your first Lisp programs will involve the use of character strings. In this section,\nwe will cover the basics: creating strings, concatenating strings to create new strings, for substrings\nin a string, and extracting substrings from longer strings. The string functions that we will look\nat here do not modify their arguments; rather, they return new strings as values. For efficiency,\nCommon Lisp does include destructive string functions that do modify their arguments but we will\nnot discuss these destructive functions here.\nWe saw earlier that a string is a type of vector, which in turn is a type of array (which in turn is a type\nof sequence). A full coverage of the Common Lisp type system is outside the scope of this tutorial\nintroduction to Common Lisp; a very good treatment of Common Lisp types is in Guy Steele’s\n“Common Lisp, The Language” which is available both in print and for free on the web. Many of\nthe built in functions for handling strings are actually more general because they are defined for the\n\nCommon Lisp Basics\n27\ntype sequence. The Common Lisp Hyperspec is another great free resource that you can find on the\nweb. I suggest that you download an HTML version of Guy Steele’s excellent reference book and\nthe Common Lisp Hyperspec and keep both on your computer. If you continue using Common Lisp,\neventually you will want to read all of Steele’s book and use the Hyperspec for reference.\nThe following text was captured from input and output from a Common Lisp repl. First, we will\ndeclare two global variables s1 and space that contain string values:\n* (defvar s1 \"the cat ran up the tree\")\nS1\n* (defvar space \" \")\nSPACE\n*\nOne of the most common operations on strings is to concatenate two or more strings into a new\nstring:\n* (concatenate 'string s1 space \"up the tree\")\n\"the cat ran up the tree up the tree\"\n*\nNotice that the first argument of the function concatenate is the type of the sequence that the\nfunction should return; in this case, we want a string. Another common string operation is search\nfor a substring:\n* (search \"ran\" s1)\n8\n* (search \"zzzz\" s1)\nNIL\n*\nIf the search string (first argument to function search) is not found, function search returns nil,\notherwise search returns an index into the second argument string. Function search takes several\noptional keyword arguments (see the next chapter for a discussion of keyword arguments):\n(search search-string a-longer-string :from-end :test\n:test-not :key\n:start1 :start2\n:end1 :end2)\nFor our discussion, we will just use the keyword argument :start2 for specifying the starting search\nindex in the second argument string and the :from-end flag to specify that search should start at\nthe end of the second argument string and proceed backwards to the beginning of the string:\n\nCommon Lisp Basics\n28\n* (search \" \" s1)\n3\n* (search \" \" s1 :start2 5)\n7\n* (search \" \" s1 :from-end t)\n18\n*\nThe sequence function subseq can be used for strings to extract a substring from a longer string:\n* (subseq s1 8)\n\"ran up the tree\"\n>\nHere, the second argument specifies the starting index; the substring from the starting index to the\nend of the string is returned. An optional third index argument specifies one greater than the last\ncharacter index that you want to extract:\n* (subseq s1 8 11)\n\"ran\"\n*\nIt is frequently useful to remove white space (or other) characters from the beginning or end of a\nstring:\n* (string-trim '(#\\space #\\z #\\a) \" a boy said pez\")\n\"boy said pe\"\n*\nThe character #\\space is the space character. Other common characters that are trimmed are #\\tab\nand #\\newline. There are also utility functions for making strings upper or lower case:\n* (string-upcase \"The dog bit the cat.\")\n\"THE DOG BIT THE CAT.\"\n* (string-downcase \"The boy said WOW!\")\n\"the boy said wow!\"\n>\nWe have not yet discussed equality of variables. The function eq returns true if two variables refer\nto the same data in memory. The function eql returns true if the arguments refer to the same data\nin memory or if they are equal numbers or characters. The function equal is more lenient: it returns\ntrue if two variables print the same when evaluated. More formally, function equal returns true if\nthe car and cdr recursively equal to each other. An example will make this clearer:\n\nCommon Lisp Basics\n29\n* (defvar x '(1 2 3))\nX\n* (defvar y '(1 2 3))\nY\n* (eql x y)\nNIL\n* (equal x y)\nT\n* x\n(1 2 3)\n* y\n(1 2 3)\n*\nFor strings, the function string= is slightly more efficient than using the function equal:\n* (eql \"cat\" \"cat\")\nNIL\n* (equal \"cat\" \"cat\")\nT\n* (string= \"cat\" \"cat\")\nT\n*\nCommon Lisp strings are sequences of characters. The function char is used to extract individual\ncharacters from a string:\n* s1\n\"the cat ran up the tree\"\n* (char s1 0)\n#\\t\n* (char s1 1)\n#\\h\n*\nUsing Hash Tables\nHash tables are an extremely useful data type. While it is true that you can get the same effect by\nusing lists and the assoc function, hash tables are much more efficient than lists if the lists contain\nmany elements. For example:\n\nCommon Lisp Basics\n30\n* (defvar x '((1 2) (\"animal\" \"dog\")))\nX\n* (assoc 1 x)\n(1 2)\n* (assoc \"animal\" x)\nNIL\n* (assoc \"animal\" x :test #'equal)\n(\"animal\" \"dog\")\n*\nThe second argument to function assoc is a list of cons cells. Function assoc searches for a sub-list\n(in the second argument) that has its car (i.e., first element) equal to the first argument to function\nassoc. The perhaps surprising thing about this example is that assoc seems to work with an integer\nas the first argument but not with a string. The reason for this is that by default the test for equality\nis done with eql that tests two variables to see if they refer to the same memory location or if they\nare identical if they are numbers. In the last call to assoc we used “:test #’equal” to make assoc use\nthe function equal to test for equality.\nThe problem with using lists and assoc is that they are very inefficient for large lists. We will see\nthat it is no more difficult to code with hash tables.\nA hash table stores associations between key and value pairs, much like our last example using the\nassoc function. By default, hash tables use eql to test for equality when looking for a key match. We\nwill duplicate the previous example using hash tables:\n* (defvar h (make-hash-table))\nH\n* (setf (gethash 1 h) 2)\n2\n* (setf (gethash \"animal\" h) \"dog\")\n\"dog\"\n* (gethash 1 h)\n2 ;\nT\n* (gethash \"animal\" h)\nNIL ;\nNIL\n*\nNotice that gethash returns multiple values: the first value is the value matching the key passed as\nthe first argument to function gethash and the second returned value is true if the key was found\nand nil otherwise. The second returned value could be useful if hash values are nil.\n\nCommon Lisp Basics\n31\nSince we have not yet seen how to handle multiple returned values from a function, we will digress\nand do so here (there are many ways to handle multiple return values and we are just covering one\nof them):\n* (multiple-value-setq (a b) (gethash 1 h))\n2\n* a\n2\n* b\nT\n*\nAssuming that variables a and b are already declared, the variable a will be set to the first returned\nvalue from gethash and the variable b will be set to the second returned value.\nIf we use symbols as hash table keys, then using eql for testing for equality with hash table keys is\nfine:\n* (setf (gethash 'bb h) 'aa)\nAA\n* (gethash 'bb h)\nAA ;\nT\n*\nHowever, we saw that eql will not match keys with character string values. The function make-\nhash-table has optional key arguments and one of them will allow us to use strings as hash key\nvalues:\n(make-hash-table &key :test :size :rehash-size :rehash-threshold)\nHere, we are only interested in the first optional key argument :test that allows us to use the function\nequal to test for equality when matching hash table keys. For example:\n\nCommon Lisp Basics\n32\n* (defvar h2 (make-hash-table :test #'equal))\nH2\n* (setf (gethash \"animal\" h2) \"dog\")\n\"dog\"\n* (setf (gethash \"parrot\" h2) \"Brady\")\n\"Brady\"\n* (gethash \"parrot\" h2)\n\"Brady\" ;\nT\n*\nIt is often useful to be able to enumerate all the key and value pairs in a hash table. Here is a simple\nexample of doing this by first defining a function my-print that takes two arguments, a key and a\nvalue. We can then use the maphash function to call our new function my-print with every key\nand value pair in a hash table:\n* (defun my-print (a-key a-value)\n(format t \"key: ~A value: ~A~\\%\" a-key a-value))\nMY-PRINT\n* (maphash #'my-print h2)\nkey: parrot value: Brady\nkey: animal value: dog\nNIL\n*\nThe function my-print is applied to each key/value pair in the hash table. There are a few other\nuseful hash table functions that we demonstrate here:\n* (hash-table-count h2)\n2\n* (remhash \"animal\" h2)\nT\n* (hash-table-count h2)\n1\n* (clrhash h2)\n#S(HASH-TABLE EQUAL)\n* (hash-table-count h2)\n0\n*\nThe function hash-table-count returns the number of key and value pairs in a hash table. The\nfunction remhash can be used to remove a single key and value pair from a hash table. The function\nclrhash clears out a hash table by removing all key and value pairs in a hash table.\n\nCommon Lisp Basics\n33\nIt is interesting to note that clrhash and remhash are the first Common Lisp functions that we\nhave seen so far that modify any of its arguments, except for setq and setf that are macros and not\nfunctions.\nUsing Eval to Evaluate Lisp Forms\nWe have seen how we can type arbitrary Lisp expressions in the Lisp repl listener and then they are\nevaluated. We will see in the Chapter on Input and Output that the Lisp function read evaluates\nlists (or forms) and indeed the Lisp repl uses function read.\nIn this section, we will use the function eval to evaluate arbitrary Lisp expressions inside a program.\nAs a simple example:\n* (defvar x '(+ 1 2 3 4 5))\nX\n* x\n(+ 1 2 3 4 5)\n* (eval x)\n15\n*\nUsing the function eval, we can build lists containing Lisp code and evaluate generated code inside\nour own programs. We get the effect of “data is code”. A classic Lisp program, the OPS5 expert\nsystem tool, stored snippets of Lisp code in a network data structure and used the function eval to\nexecute Lisp code stored in the network. A warning: the use of eval is likely to be inefficient in\nnon-compiled code. For efficiency, the OPS5 program contained its own version of eval that only\ninterpreted a subset of Lisp used in the network.\nUsing a Text Editor to Edit Lisp Source Files\nI usually use Emacs, but we will briefly discuss the editor vi also. If you use vi (e.g., enter “vi\nnested.lisp”) the first thing that you should do is to configure vi to indicate matching opening\nparentheses whenever a closing parentheses is typed; you do this by typing “:set sm” after vi is\nrunning.\nIf you choose to learn Emacs, enter the following in your .emacs file (or your _emacs file in your\nhome directory if you are running Windows):\n\nCommon Lisp Basics\n34\n1\n(set-default 'auto-mode-alist\n2\n(append '((\"\\\\.lisp$\" . lisp-mode)\n3\n(\"\\\\.lsp$\" . lisp-mode)\n4\n(\"\\\\.cl$\" . lisp-mode))\n5\nauto-mode-alist))\nNow, whenever you open a file with the extension of “lisp”, “lsp”, or “cl” (for “Common Lisp”) then\nEmacs will automatically use a Lisp editing mode. I recommend searching the web using keywords\n“Emacs tutorial” to learn how to use the basic Emacs editing commands - we will not repeat this\ninformation here.\nI do my professional Lisp programming using free software tools: Emacs, SBCL, Clozure Common\nLisp, and Clojure. I will show you how to configure Emacs and Slime in the last section of the\nChapter on Quicklisp.\nRecovering from Errors\nWhen you enter forms (or expressions) in a Lisp repl listener, you will occasionally make a mistake\nand an error will be thrown. Here is an example where I am not showing all of the output when\nentering help when an error is thrown:\n* (defun my-add-one (x) (+ x 1))\nMY-ADD-ONE\n* (my-add-one 10)\n11\n* (my-add-one 3.14159)\n4.14159\n* (my-add-one \"cat\")\ndebugger invoked on a SIMPLE-TYPE-ERROR: Argument X is not a NUMBER: \"cat\"\nType HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.\nrestarts (invokable by number or by possibly-abbreviated name):\n0: [ABORT] Exit debugger, returning to top level.\n(SB-KERNEL:TWO-ARG-+ \"cat\" 1)\n0] help\n\nCommon Lisp Basics\n35\nThe debug prompt is square brackets, with number(s) indicating the current\ncontrol stack level and, if you've entered the debugger recursively, how\ndeeply recursed you are.\n...\nGetting in and out of the debugger:\nTOPLEVEL, TOP\nexits debugger and returns to top level REPL\nRESTART\ninvokes restart numbered as shown (prompt if not given).\nERROR\nprints the error condition and restart cases.\n...\nInspecting frames:\nBACKTRACE [n]\nshows n frames going down the stack.\nLIST-LOCALS, L lists locals in current frame.\nPRINT, P\ndisplays function call for current frame.\nSOURCE [n]\ndisplays frame's source form with n levels of enclosing forms.\nStepping:\nSTART Selects the CONTINUE restart if one exists and starts\nsingle-stepping. Single stepping affects only code compiled with\nunder high DEBUG optimization quality. See User Manual for details.\nSTEP\nSteps into the current form.\nNEXT\nSteps over the current form.\nOUT\nStops stepping temporarily, but resumes it when the topmost frame that\nwas stepped into returns.\nSTOP\nStops single-stepping.\n...\n0] list-locals\nSB-DEBUG::ARG-0\n=\n\"cat\"\nSB-DEBUG::ARG-1\n=\n1\n0] backtrace 2\nBacktrace for: #<SB-THREAD:THREAD \"main thread\" RUNNING {1002AC32F3}>\n0: (SB-KERNEL:TWO-ARG-+ \"cat\" 1)\n1: (MY-ADD-ONE \"cat\")\n0] :0\n*\n\nCommon Lisp Basics\n36\nHere, I first used the backtrace command :bt to print the sequence of function calls that caused the\nerror. If it is obvious where the error is in the code that I am working on then I do not bother using\nthe backtrace command. I then used the abort command :a to recover back to the top level Lisp\nlistener (i.e., back to the greater than prompt). Sometimes, you must type :a more than once to fully\nrecover to the top level greater than prompt.\nGarbage Collection\nLike other languages like Java and Python, Common Lisp provides garbage collection (GC) or\nautomatic memory management.\nIn simple terms, GC occurs to free memory in a Lisp environment that is no longer accessible by any\nglobal variable (or function closure, which we will cover in the next chapter). If a global variable\n*variable-1* is first set to a list and then if we later then set *variable-1* to, for example nil, and if\nthe data referenced in the original list is not referenced by any other accessible data, then this now\nunused data is subject to GC.\nIn practice, memory for Lisp data is allocated in time ordered batches and ephemeral or generational\ngarbage collectors garbage collect recent memory allocations far more often than memory that has\nbeen allocated for a longer period of time.\nLoading your Working Environment Quickly\nWhen you start using Common Lisp for large projects, you will likely have many files to load\ninto your Lisp environment when you start working. Most Common Lisp implementations have\na function called defsystem that works somewhat like the Unix make utility. While I strongly\nrecommend defsystem for large multi-person projects, I usually use a simpler scheme when working\non my own: I place a file loadit.lisp in the top directory of each project that I work on. For any project,\nits loadit.lisp file loads all source files and initializes any global data for the project.\nThe last two chapters of this book provide example applications that are configured to work with\nQuicklisp, which we will study in the next chapter.\nAnother good technique is to create a Lisp image containing all the code and data for all your projects.\nThere is an example of this in the first section of the Chapter on NLP. In this example, it takes a few\nminutes to load the code and data for my NLP (natural language processing) library so when I am\nworking with it I like to be able to quickly load a SBCL Lisp image.\nAll Common Lisp implementations have a mechanism for dumping a working image containing\ncode and data.\n\nCommon Lisp Basics\n37\nFunctional Programming Concepts\nThere are two main styles for doing Common Lisp development. Object oriented programming is\nwell supported (see the Chapter on CLOS) as is functional programming. In a nut shell, functional\nprogramming means that we should write functions with no side effects. First let me give you a\nnon-functional example with side effects:\n(defun non-functional-example (car)\n(set-color car \"red\"))\nThis example using CLOS is non-functional because we modify the value of an argument to\nthe function. Some functional languages like the Lisp Clojure language and the Haskell language\ndissuade you from modifying arguments to functions. With Common Lisp you should make a\ndecision on which approach you like to use.\nFunctional programming means that we avoid maintaining state inside of functions and treat data\nas immutable (i.e., once an object is created, it is never modified). We could modify the last example\nto be function by creating a new car object inside the function, copy the attributes of the car passed\nas an object, change the color to “red” of the new car object, and return the new car instance as the\nvalue of the function.\nFunctional programming prevents many types of programming errors, makes unit testing simpler,\nand makes programming for modern multi-core CPUs easier because read-only objects are inher-\nently thread safe. Modern best practices for the Java language also prefer immutable data objects\nand a functional approach.\n\nQuicklisp\nFor several decades managing packages and libraries was a manual process when developing Lisp\nsystems. I used to package the source code for specific versions of libraries as part of my Common\nLisp projects. Early package management systems mk-defsystem and ASDF were very useful, but I\ndid not totally give up my practice keeping third party library source code with my projects until\nZach Beane created the Quicklisp package system²⁹. You will need to have Quicklisp installed for\nmany of the examples later in this book so please take the time to install it now as per the instructions\non the Quicklisp web site.\nUsing Quicklisp to Find Packages\nWe will need the Common Lisp Hunchentoot library later in the Chapter on Network Programming\nso we will install it now using Quicklisp as an example for getting started with Quicklisp.\nWe already know the package name we want, but as an example of discovering packages let’s start\nby using Quicklisp to search for all packages with “hunchentoot” in the package name:\n1\n* (ql:system-apropos \"hunchentoot\")\n2\n#<SYSTEM clack-handler-hunchentoot / clack-20131111-git / quicklisp 2013-11-11>\n3\n#<SYSTEM hunchentoot / hunchentoot-1.2.21 / quicklisp 2013-11-11>\n4\n#<SYSTEM hunchentoot-auth / hunchentoot-auth-20101107-git / quicklisp 2013-11-11>\n5\n#<SYSTEM hunchentoot-cgi / hunchentoot-cgi-20121125-git / quicklisp 2013-11-11>\n6\n#<SYSTEM hunchentoot-dev / hunchentoot-1.2.21 / quicklisp 2013-11-11>\n7\n#<SYSTEM hunchentoot-single-signon / hunchentoot-single-signon-20131111-git / quickl\\\n8\nisp 2013-11-11>\n9\n#<SYSTEM hunchentoot-test / hunchentoot-1.2.21 / quicklisp 2013-11-11>\n10\n#<SYSTEM hunchentoot-vhost / hunchentoot-vhost-20110418-git / quicklisp 2013-11-11>\nWe want the base package seen in line 3 and we can install the base package as seen in the following\nexample:\n²⁹http://www.quicklisp.org/\n\nQuicklisp\n39\n1\n* (ql:quickload :hunchentoot)\n2\nTo load \"hunchentoot\":\n3\nLoad 1 ASDF system:\n4\nhunchentoot\n5\n; Loading \"hunchentoot\"\n6\n.......\n7\n(:HUNCHENTOOT)\nIn line 1, I refer to the package name using a symbol :hunchentoot but using the string “hunchentoot”\nwould have worked the same. The first time you ql:quickload a library you may see additional\nprintout and it takes longer to load because the source code is downloaded from the web and cached\nlocally in the directory ∼/quicklisp/local-projects. In most of the rest of this book, when I install\nor use a package by calling the ql:quickload function I do not show the output from this function\nin the repl listings.\nNow, we can use the fantastically useful Common Lisp function apropos to see what was just\ninstalled:\n1\n* (apropos \"hunchentoot\")\n2\n3\nHUNCHENTOOT::*CLOSE-HUNCHENTOOT-STREAM* (bound)\n4\nHUNCHENTOOT:*HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT* (bound)\n5\nHUNCHENTOOT::*HUNCHENTOOT-STREAM*\n6\nHUNCHENTOOT:*HUNCHENTOOT-VERSION* (bound)\n7\nHUNCHENTOOT:HUNCHENTOOT-CONDITION\n8\nHUNCHENTOOT:HUNCHENTOOT-ERROR (fbound)\n9\nHUNCHENTOOT::HUNCHENTOOT-OPERATION-NOT-IMPLEMENTED-OPERATION (fbound)\n10\nHUNCHENTOOT::HUNCHENTOOT-SIMPLE-ERROR\n11\nHUNCHENTOOT::HUNCHENTOOT-SIMPLE-WARNING\n12\nHUNCHENTOOT::HUNCHENTOOT-WARN (fbound)\n13\nHUNCHENTOOT:HUNCHENTOOT-WARNING\n14\nHUNCHENTOOT-ASD:*HUNCHENTOOT-VERSION* (bound)\n15\nHUNCHENTOOT-ASD::HUNCHENTOOT\n16\n:HUNCHENTOOT (bound)\n17\n:HUNCHENTOOT-ASD (bound)\n18\n:HUNCHENTOOT-DEV (bound)\n19\n:HUNCHENTOOT-NO-SSL (bound)\n20\n:HUNCHENTOOT-TEST (bound)\n21\n:HUNCHENTOOT-VERSION (bound)\n22\n*\nAs long as you are thinking about the new tool Quicklisp that is now in your tool chest, you should\ninstall most of the packages and libraries that you will need for working through the rest of this\n\nQuicklisp\n40\nbook. I will show the statements needed to load more libraries without showing the output printed\nin the repl as each package is loaded:\n1\n(ql:quickload \"clsql\")\n2\n(ql:quickload \"clsql-postgresql\")\n3\n(ql:quickload \"clsql-mysql\")\n4\n(ql:quickload \"clsql-sqlite3\")\n5\n(ql:quickload :drakma)\n6\n(ql:quickload :hunchentoot)\n7\n(ql:quickload :cl-json)\n8\n(ql:quickload \"clouchdb\")\n;; for CouchDB access\n9\n(ql:quickload \"sqlite\")\nYou need to have the Postgres and MySQL client developer libraries installed on your system for\nthe clsql-postgresql and clsql-mysql installations to work. If you are unlikely to use relational\ndatabases with Common Lisp then you might skip the effort of installing Postgres and MySQL. The\nexample in the Chapter on the Knowledge Graph Navigator uses the SQLite database for caching.\nYou don’t need any extra dependencies for the sqlite package.\nUsing Quicklisp to Configure Emacs and Slime\nI assume that you have Emacs installed on your system. In a repl you can setup the Slime package\nthat allows Emacs to connect to a running Lisp environment:\n(ql:quickload \"quicklisp-slime-helper\")\nPay attention to the output in the repl. On my system the output contained the following:\n1\n[package quicklisp-slime-helper]\n2\nslime-helper.el installed in \"/Users/markw/quicklisp/slime-helper.el\"\n3\n4\nTo use, add this to your ~/.emacs:\n5\n6\n(load (expand-file-name \"~/quicklisp/slime-helper.el\"))\n7\n;; Replace \"sbcl\" with the path to your implementation\n8\n(setq inferior-lisp-program \"sbcl\")\nIf you installed rlwrap and defined an alias for running SBCL, make sure you set the inferior lisp\nprogram to the absolute path of the SBCL executable; on my system I set the following in my .emacs\nfile:\n\nQuicklisp\n41\n1\n(setq inferior-lisp-program \"/Users/markw/sbcl/sbcl\")\nI am not going to cover using Emacs and Slime, there are many good tutorials on the web you can\nread.\nIn later chapters we will write libraries and applications as Quicklisp projects so that you will be\nable to load your own libraries, making it easier to write small libraries that you can compose into\nlarger applications.\n\nDefining Lisp Functions\nIn the previous chapter, we defined a few simple functions. In this chapter, we will discuss how\nto write functions that take a variable number of arguments, optional arguments, and keyword\narguments.\nThe special form defun is used to define new functions either in Lisp source files or at the top level\nLisp listener prompt. Usually, it is most convenient to place function definitions in a source file and\nuse the function load to load them into our Lisp working environment.\nIn general, it is bad form to use global variables inside Lisp functions. Rather, we prefer to pass all\nrequired data into a function via its argument list and to get the results of the function as the value\n(or values) returned from a function. Note that if we do require global variables, it is customary to\nname them with beginning and ending * characters; for example:\n1\n(defvar *lexical-hash-table*\n2\n(make-hash-table :test #'equal :size 5000))\nThen in this example, if you see the variable *lexical-hash-table* inside a function definition, you\nwill know that at least by naming convention, that this is a global variable.\nIn Chapter 1, we saw an example of using lexically scoped local variables inside a function definition\n(in the example file nested.lisp).\nThere are several options for defining the arguments that a function can take. The fastest way to\nintroduce the various options is with a few examples.\nFirst, we can use the &aux keyword to declare local variables for use in a function definition:\n1\n* (defun test (x &aux y)\n2\n(setq y (list x x))\n3\ny)\n4\nTEST\n5\n* (test 'cat)\n6\n(CAT CAT)\n7\n* (test 3.14159)\n8\n(3.14159 3.14159)\nIt is considered better coding style to use the let special operator for defining auxiliary local variables;\nfor example:\n\nDefining Lisp Functions\n43\n1\n* (defun test (x)\n2\n(let ((y (list x x)))\n3\ny))\n4\nTEST\n5\n* (test \"the dog bit the cat\")\n6\n(\"the dog bit the cat\" \"the dog bit the cat\")\n7\n*\nYou will probably not use &aux very often, but there are two other options for specifying function\narguments: &optional and &key.\nThe following code example shows how to use optional function arguments. Note that optional\narguments must occur after required arguments.\n1\n* (defun test (a &optional b (c 123))\n2\n(format t \"a=~A b=~A c=~A~%\" a b c))\n3\nTEST\n4\n* (test 1)\n5\na=1 b=NIL c=123\n6\nNIL\n7\n* (test 1 2)\n8\na=1 b=2 c=123\n9\nNIL\n10\n* (test 1 2 3)\n11\na=1 b=2 c=3\n12\nNIL\n13\n* (test 1 2 \"Italian Greyhound\")\n14\na=1 b=2 c=Italian Greyhound\n15\nNIL\n16\n*\nIn this example, the optional argument b was not given a default value so if unspecified it will default\nto nil. The optional argument c is given a default value of 123.\nWe have already seen the use of keyword arguments in built-in Lisp functions. Here is an example\nof how to specify key word arguments in your functions:\n\nDefining Lisp Functions\n44\n1\n* (defun test (a &key b c)\n2\n(format t \"a=~A b=~A c=~A~%\" a b c))\n3\nTEST\n4\n* (test 1)\n5\na=1 b=NIL c=NIL\n6\nNIL\n7\n* (test 1 :c 3.14159)\n8\na=1 b=NIL c=3.14159\n9\nNIL\n10\n* (test \"cat\" :b \"dog\")\n11\na=cat b=dog c=NIL\n12\nNIL\n13\n*\nUsing Lambda Forms\nIt is often useful to define unnamed functions. We can define an unnamed function using lambda;\nfor example, let’s look at the example file src/lambda1.lisp. But first, we will introduce the Common\nLisp function funcall that takes one or more arguments; the first argument is a function and any\nremaining arguments are passed to the function bound to the first argument. For example:\n1\n* (funcall 'print 'cat)\n2\nCAT\n3\nCAT\n4\n* (funcall '+ 1 2)\n5\n3\n6\n* (funcall #'- 2 3)\n7\n-1\n8\n*\nIn the first two calls to funcall here, we simply quote the function name that we want to call. In\nthe third example, we use a better notation by quoting with #’. We use the #’ characters to quote a\nfunction name.\nConsider the following repl listing where we will look at a primary difference between quoting a\nsymbol using ‘ and with #’:\n\nDefining Lisp Functions\n45\n1\n$ ccl\n2\nClozure Common Lisp Version 1.12\nDarwinX8664\n3\n? 'barfoo531\n4\nBARFOO531\n5\n? (apropos \"barfoo\")\n6\nBARFOO531\n7\n? #'bar987\n8\n> Error: Undefined function: BAR987\nOn line three we create a new symbol BARFOO531 that is interned as you can see from looking\nat all interned symbols containing the string “barfoo”. Line 7 throws an error because #’ does not\nintern a new symbol.\nHere is the example file src/lambda1.lisp:\n1\n(defun test ()\n2\n(let ((my-func\n3\n(lambda (x) (+ x 1))))\n4\n(funcall my-func 1)))\nHere, we define a function using lambda and set the value of the local variable my-func to the\nunnamed function’s value. Here is output from the function test:\n1\n* (test)\n2\n2\n3\n4\n*\nThe ability to use functions as data is surprisingly useful. For now, we will look at a simple example:\n1\n* (defvar f1 #'(lambda (x) (+ x 1)))\n2\n3\nF1\n4\n* (funcall f1 100)\n5\n6\n101\n7\n* (funcall #'print 100)\n8\n9\n100\n10\n100\nNotice that the second call to function testfn prints “100” twice: the first time as a side effect of\ncalling the function print and the second time as the returned value of testfn (the function print\nreturns what it is printing as its value).\n\nDefining Lisp Functions\n46\nUsing Recursion\nLater, we will see how to use special Common Lisp macros for programming repetitive loops. In this\nsection, we will use recursion for both coding simple loops and as an effective way to solve a variety\nof problems that can be expressed naturally using recursion.\nAs usual, the example programs for this section are found in the src directory. In the file\nsrc/recursion1.lisp, we see our first example of recursion:\n1\n;; a simple loop using recursion\n2\n3\n(defun recursion1 (value)\n4\n(format t \"entering recursion1(~A)~\\%\" value)\n5\n(if (< value 5)\n6\n(recursion1 (1+ value))))\nThis example is simple, but it is useful for discussing a few points. First, notice how the function\nrecursion1 calls itself with an argument value of one greater than its own input argument only if\nthe input argument “value” is less than 5. This test keeps the function from getting in an infinite\nloop. Here is some sample output:\n1\n* (load \"recursion1.lisp\")\n2\n;; Loading file recursion1.lisp ...\n3\n;; Loading of file recursion1.lisp is finished.\n4\nT\n5\n* (recursion1 0)\n6\nentering recursion1(0)\n7\nentering recursion1(1)\n8\nentering recursion1(2)\n9\nentering recursion1(3)\n10\nentering recursion1(4)\n11\nentering recursion1(5)\n12\nNIL\n13\n* (recursion1 -3)\n14\nentering recursion1(-3)\n15\nentering recursion1(-2)\n16\nentering recursion1(-1)\n17\nentering recursion1(0)\n18\nentering recursion1(1)\n19\nentering recursion1(2)\n20\nentering recursion1(3)\n21\nentering recursion1(4)\n\nDefining Lisp Functions\n47\n22\nentering recursion1(5)\n23\nNIL\n24\n* (recursion1 20)\n25\nentering recursion1(20)\n26\nNIL\n27\n*\nWhy did the call on line 24 not loop via recursion? Because the input argument is not less than 5, no\nrecursion occurs.\nClosures\nWe have seen that functions can take other functions as arguments and return new functions as\nvalues. A function that references an outer lexically scoped variable is called a closure. The example\nfile src/closure1.lisp contains a simple example:\n1\n(let* ((fortunes\n2\n'(\"You will become a great Lisp Programmer\"\n3\n\"The force will not be with you\"\n4\n\"Take time for meditation\"))\n5\n(len (length fortunes))\n6\n(index 0))\n7\n(defun fortune ()\n8\n(let ((new-fortune (nth index fortunes)))\n9\n(setq index (1+ index))\n10\n(if (>= index len) (setq index 0))\n11\nnew-fortune)))\nHere the function fortune is defined inside a let form. Because the local variable fortunes is\nreferenced inside the function fortune, the variable fortunes exists after the let form is evaluated. It\nis important to understand that usually a local variable defined inside a let form “goes out of scope”\nand can no longer be referenced after the let form is evaluated.\nHowever, in this example, there is no way to access the contents of the variable fortunes except by\ncalling the function fortune. At a minimum, closures are a great way to hide variables. Here is some\noutput from loading the src/closure1.lisp file and calling the function fortune several times:\n\nDefining Lisp Functions\n48\n1\n* (load \"closure1.lisp\")\n2\n;; Loading file closure1.lisp ...\n3\n;; Loading of file closure1.lisp is finished.\n4\nT\n5\n* (fortune)\n6\n\"You will become a great Lisp Programmer\"\n7\n* (fortune)\n8\n\"The force will not be with you\"\n9\n* (fortune)\n10\n\"Take time for meditation\"\n11\n* (fortune)\n12\n\"You will become a great Lisp Programmer\"\n13\n*\nUsing the Function eval\nIn Lisp languages we often say that code is data. The function eval can be used to execute code that\nis stored as Lisp data. Let’s look at an example:\n1\n$ ccl\n2\nClozure Common Lisp Version 1.12\nDarwinX8664\n3\n? '(+ 1 2.2)\n4\n(+ 1 2.2)\n5\n? (eval '(+ 1 2.2))\n6\n3.2\n7\n? (eval '(defun foo2 (x) (+ x x)))\n8\nFOO2\n9\n? (foo2 4)\n10\n8\nI leave it up to you, dear reader, how often you are motivated to use eval. In forty years of using\nLisp languages my principle use of eval has been in modifying the standard version of the Ops5\nprogramming language for production systems³⁰ to support things like multiple data worlds and\nnew actions to spawn off new data worlds and to remove them. Ops5 works by finding common\nexpressions in a set of production rules (also referred to as “expert systems”) and factoring them into\na network (a Rete network if you want to look it up) with common expressions in rules stored in\njust a single place. eval is used a lot in Ops5 and I used it for my extensions to Ops5.\n³⁰https://github.com/sharplispers/ops5\n\nDefining Common Lisp Macros\nWe saw in the last chapter how the Lisp function eval could be used to evaluate arbitrary Lisp code\nstored in lists. Because eval is inefficient, a better way to generate Lisp code automatically is to define\nmacro expressions that are expanded inline when they are used. In most Common Lisp systems,\nusing eval requires the Lisp compiler to compile a form on-the-fly which is not very efficient. Some\nLisp implementations use an interpreter for eval which is likely to be faster but might lead to obscure\nbugs if the interpreter and compiled code do not function identically.\nThe ability to add functionality and syntax to the Common Lisp language, to in effect extend the\nlanguage as needed, is truly a super power of languages like Common Lisp and Scheme.\nExample Macro\nThe file src/macro1.lisp contains both a simple macro and a function that uses the macro. This\nmacro example is a bit contrived since it could be just a function definition, but it does show the\nprocess of creating and using a macro. We are using the gensym function to define a new unique\nsymbol to reference a temporary variable:\n1\n;; first simple macro example:\n2\n3\n(defmacro double-list (a-list)\n4\n(let ((ret (gensym)))\n5\n`(let ((,ret nil))\n6\n(dolist (x ,a-list)\n7\n(setq ,ret (append ,ret (list x x))))\n8\n,ret)))\n9\n10\n;; use the macro:\n11\n12\n(defun test (x)\n13\n(double-list x))\nThe backquote character seen at the beginning of line 5 is used to quote a list in a special way:\nnothing in the list is evaluated during macro expansion unless it is immediately preceded by a\ncomma character. In this case, we specify ,a-list because we want the value of the macro’s argument\na-list to be substituted into the specially quoted list. We will look at dolist in some detail in the next\nchapter but for now it is sufficient to understand that dolist is used to iterate through the top-level\nelements of a list, for example:\n\nDefining Common Lisp Macros\n50\n1\n* (dolist (x '(\"the\" \"cat\" \"bit\" \"the\" \"rat\"))\n2\n(print x))\n3\n\"the\"\n4\n\"cat\"\n5\n\"bit\"\n6\n\"the\"\n7\n\"rat\"\n8\nNIL\n9\n*\nNotice that the example macro double-list itself uses the macro dolist. It is common to nest macros\nin the same way functions can be nested.\nReturning to our macro example in the file src/macro1.lisp, we will try the function test that uses\nthe macro double-list:\n1\n* (load \"macro1.lisp\")\n2\n;; Loading file macro1.lisp ...\n3\n;; Loading of file macro1.lisp is finished.\n4\nT\n5\n* (test '(1 2 3))\n6\n(1 1 2 2 3 3)\n7\n*\nUsing the Splicing Operator\nAnother similar example is in the file src/macro2.lisp:\n1\n;; another macro example that uses ,@:\n2\n3\n(defmacro double-args (&rest args)\n4\n`(let ((ret nil))\n5\n(dolist (x ,@args)\n6\n(setq ret (append ret (list x x))))\n7\nret))\n8\n9\n;; use the macro:\n10\n11\n(defun test (&rest x)\n12\n(double-args x))\nHere, the splicing operator ,@ is used to substitute in the list args in the macro double-args.\n\nDefining Common Lisp Macros\n51\nUsing macroexpand-1\nThe function macroexpand-1 is used to transform macros with arguments into new Lisp expressions.\nFor example:\n1\n* (defmacro double (a-number)\n2\n(list '+ a-number a-number))\n3\nDOUBLE\n4\n* (macroexpand-1 '(double n))\n5\n(+ N N) ;\n6\nT\n7\n*\nWriting macros is an effective way to extend the Lisp language because you can control the code\npassed to the Common Lisp compiler. In both macro example files, when the function test was\ndefined, the macro expansion is done before the compiler processes the code. We will see in the next\nchapter several useful macros included in Common Lisp.\nWe have only “scratched the surface” looking at macros; the interested reader is encouraged to\nsearch the web using, for example, “Common Lisp macros.” There are two books in particular that\nI recommend that take a deep dive into Common Lisp macros: Paul Graham’s “On Lisp” and Doug\nHoyte’s “Let Over Lambda.” Both are deep books and will change the way you experience software\ndevelopment. A good plan of study is spending a year absorbing “On Lisp” before tackling “Let Over\nLambda.”\n\nUsing Common Lisp Loop Macros\nIn this chapter, we will discuss several useful macros for performing iteration (we saw how to use\nrecursion for iteration in Chapter 2):\n• dolist – a simple way to process the elements of a list\n• dotimes – a simple way to iterate with an integer valued loop variable\n• do – the most general looping macro\n• loop – a complex looping macro that I almost never use in my own code because it does not\nlook “Lisp like.” I don’t use the loop macro in this book. Many programmers do like the loop\nmacro so you are likely to see it when reading other people’s code.\ndolist\nWe saw a quick example of dolist in the last chapter. The arguments of the dolist macro are:\n(dolist (a-variable a-list [optional-result-value])\n...body... )\nUsually, the dolist macro returns nil as its value, but we can add a third optional argument which\nwill be returned as the generated expression’s value; for example:\n1\n* (dolist (a '(1 2) 'done) (print a))\n2\n1\n3\n2\n4\nDONE\n5\n* (dolist (a '(1 2)) (print a))\n6\n1\n7\n2\n8\nNIL\n9\n*\nThe first argument to the dolist macro is a local lexically scoped variable. Once the code generated\nby the dolist macro finishes executing, this variable is undefined.\ndotimes\nThe dotimes macro is used when you need a loop with an integer loop index. The arguments of the\ndotimes macro are:\n\nUsing Common Lisp Loop Macros\n53\n(dotimes (an-index-variable max-index-plus-one [optional-result-value])\n...body... )\nUsually, the dotimes macro returns nil as its value, but we can add a third optional argument that\nwill be returned as the generated expression’s value; for example:\n1\n* (dotimes (i 3 \"all-done-with-test-dotimes-loop\") (print i))\n2\n3\n0\n4\n1\n5\n2\n6\n\"all-done-with-test-dotimes-loop\"\n7\n*\nAs with the dolist macro, you will often use a let form inside a dotimes macro to declare additional\ntemporary (lexical) variables.\ndo\nThe do macro is more general purpose than either dotimes or dolist but it is more complicated to\nuse. Here is the general form for using the do looping macro:\n(do ((variable-1 variable-1-init-value variable-1-update-expression)\n(variable-2 variable-2-init-value variable-2-update-expression)\n.\n.\n(variable-N variable-N-init-value variable-N-update-expression))\n(loop-termination-test\nloop-return-value)\noptional-variable-declarations\nexpressions-to-be-executed-inside-the-loop)\nThere is a similar macro do* that is analogous to let* in that loop variable values can depend on the\nvalues or previously declared loop variable values.\nAs a simple example, here is a loop to print out the integers from 0 to 3. This example is in the file\nsrc/do1.lisp:\n;; example do macro use\n\nUsing Common Lisp Loop Macros\n54\n(do ((i 0 (1+ i)))\n((> i 3) \"value-of-do-loop\")\n(print i))\nIn this example, we only declare one loop variable so we might as well as used the simpler dotimes\nmacro.\nHere we load the file src/do1.lisp:\n1\n* (load \"do1.lisp\")\n2\n;; Loading file do1.lisp ...\n3\n0\n4\n1\n5\n2\n6\n3\n7\n;; Loading of file do1.lisp is finished.\n8\nT\n9\n*\nYou will notice that we do not see the return value of the do loop (i.e., the string “value-of-do-loop”)\nbecause the top-level form that we are evaluating is a call to the function load; we do see the return\nvalue of load printed. If we had manually typed this example loop in the Lisp listener, then you\nwould see the final value value-of-do-loop printed.\nUsing the loop Special Form to Iterate Over Vectors or\nArrays\nWe previousely used dolist to iterate over elements in lists. For efficiency we will often use vectors\n(one dimensional arrays) and we can use loop to similarly handle vectors:\n(loop for td across testdata\ndo\n(print td))))\nwhere testdata is a one dimensional array (a vector) and inside the do block the local variable td is\nassigned to each element in the vector.\n\nCommon Lisp Package System\nIn later chapters we will see two complete applications that are defined as Quicklisp projects: the\nchapter on the Knowledge Graph Creator and the chapter on the Knowledge Graph Navigator.\nAnother example for setting up a Quicklib project can be seen in the chapter Plotting Data.\nWhile these later chapters provide practical examples for bundling up your own projects in packages,\nthe material here will give you general background information that you should know.\nIn the simple examples that we have seen so far, all newly created Lisp symbols have been placed in\nthe default package. You can always check the current package by evaluating the expression package:\n> *package*\n#<PACKAGE COMMON-LISP-USER>\n>\nAs we will use in the following example, the package :cl is an alias for :common-lisp-user.\nWe will define a new package :my-new-package and two functions foo1 and foo2 inside the\npackage. Externally to this package, assuming that it is loaded, we can access foo2 using my-new-\npackage:foo2. foo1 is not exported so it cannot be accessed this way. However, we can always start\na symbol name with a package name and two colon characters if we want to use a symbol defined\nin another package so we can use my-new-package::foo1. Using :: allows us access to symbols not\nexplicitly exported.\nWhen I leave package :my-new-package in line 22 and return to package :cl, and try to access\nmy-new-package:foo1 notice that an error is thrown.\nOn line 3 we define the alias :p1 for the package :my-new-package and we use this alias in line\n44. The main point of the following example is that we define two functions in a package but only\nexport one of these functions. By default the other function is not visible outside of the new package.\n1\n* (defpackage \"MY-NEW-PACKAGE\"\n2\n(:use :cl)\n3\n(:nicknames \"P1\")\n4\n(:export :FOO2))\n5\n6\n#<PACKAGE \"MY-NEW-PACKAGE\">\n7\n* (in-package my-new-package)\n8\n9\n#<PACKAGE \"MY-NEW-PACKAGE\">\n10\n* (defun foo1 () \"foo1\")\n\nCommon Lisp Package System\n56\n11\n12\nFOO1\n13\n* (defun foo2 () \"foo2\")\n14\n15\nFOO2\n16\n* (foo1)\n17\n18\n\"foo1\"\n19\n* (foo2)\n20\n21\n\"foo2\"\n22\n* (in-package :cl)\n23\n24\n#<PACKAGE \"COMMON-LISP\">\n25\n* (my-new-package:foo2)\n26\n27\n\"foo2\"\n28\n* (my-new-package:foo1)\n29\n30\ndebugger invoked on a SB-INT:SIMPLE-READER-PACKAGE-ERROR in thread\n31\n#<THREAD \"main thread\" RUNNING {1001F1ECE3}>:\n32\nThe symbol \"FOO1\" is not external in the MY-NEW-PACKAGE package.\n33\n34\nStream: #<SYNONYM-STREAM :SYMBOL SB-SYS:*STDIN* {100001C343}>\n35\n36\nType HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.\n37\n38\nrestarts (invokable by number or by possibly-abbreviated name):\n39\n0: [CONTINUE] Use symbol anyway.\n40\n1: [ABORT\n] Exit debugger, returning to top level.\n41\n42\n* 1\n43\n44\n* (p1:foo2)\n45\n46\n\"foo2\"\nSince we specified a nickname in the defpackage expression, Common Lisp allows the use of the\nnickname (in this case P1) in calling function foo2 that is exported from package :my-new-package.\nNear the end of the last example, we switched back to the default package COMMON-LISP-USER\nso we had to specify the package name for the function foo2 on line 42.\nWhat about the error on line 28 where my-new-package:foo1 is undefined because the function\n\nCommon Lisp Package System\n57\nfoo1 is not exported (see line 4)? It turns out that you can easily use symbols not exported from a\npackage by using :: instead of a single :. Here, this would be defined: (my-new-package::foo1).\nWhen you are writing very large Common Lisp programs, it is useful to be able to break up\nthe program into different modules and place each module and all its required data in different\nname spaces by creating new packages. Remember that all symbols, including variables, generated\nsymbols, CLOS methods, functions, and macros are in some package.\nFor small packages I sometimes put a defpackage expression at the top of the file immediately\nfollowed by an in-package expression to switch to the new package. In the general case, please\nproperly use separate project and asdf files as I do in the later chapters Knowledge Graph Creator\nand Knowledge Graph Navigator.\n\nInput and Output\nWe will see that the input and output of Lisp data is handled using streams. Streams are powerful\nabstractions that support common libraries of functions for writing to the terminal, files, sockets,\nand to strings.\nIn all cases, if an input or output function is called without specifying a stream, the default for\ninput stream is *standard-input* and the default for output stream is *standard-output*. These\ndefault streams are connected to the Lisp listener that we discussed in Chapter 2. In the later chapter\nKnowledge Graph Navigator that supports a user interface, we will again use output streams bound\nto different scrolling output areas of the application window to write color-hilighted text. The stream\nformalism is general purpose, covering many common I/O use cases.\nThe Lisp read and read-line Functions\nThe function read is used to read one Lisp expression. Function read stops reading after reading\none expression and ignores new line characters. We will look at a simple example of reading a file\ntest.dat using the example Lisp program in the file read-test-1.lisp. Both of these files can be found\nin the directory src/code_snippets_for_book that came bundled with this web book. Start your\nLisp program in the src directory. The contents of the file test.dat is:\n1\n1 2 3\n2\n4 \"the cat bit the rat\"\n3\nread with-open-file\nIn the function read-test-1, we use the macro with-open-file to read from a file. To write to a file\n(which we will do later), we can use the keyword arguments :direction :output. The first argument\nto the macro with-open-file is a symbol that is bound to a newly created input stream (or an output\nstream if we are writing a file); this symbol can then be used in calling any function that expects a\nstream argument.\nNotice that we call the function read with three arguments: an input stream, a flag to indicate if\nan error should be thrown if there is an I/O error (e.g., reaching the end of a file), and the third\nargument is the value that function read should return if the end of the file (or stream) is reached.\nWhen calling read with these three arguments, either the next expression from the file test.dat will\nbe returned, or the value nil will be returned when the end of the file is reached. If we do reach the\nend of the file, the local variable x will be assigned the value nil and the function return will break\nout of the dotimes loop. One big advantage of using the macro with-open-file over using the open\nfunction (which we will not cover) is that the file stream is automatically closed when leaving the\ncode generated by the with-open-file macro. The contents of file read-test-1.lisp is:\n\nInput and Output\n59\n(defun read-test-1 ()\n\"read a maximum of 1000 expressions from the file 'test.dat'\"\n(with-open-file\n(input-stream \"test.dat\" :direction :input)\n(dotimes (i 1000)\n(let ((x (read input-stream nil nil)))\n(if (null x) (return)) ;; break out of the 'dotimes' loop\n(format t \"next expression in file: ~S~%\" x)))))\nHere is the output that you will see if you load the file read-test-1.lisp and execute the expression\n(read-test-1):\n1\n* (load \"read-test-1.lisp\")\n2\n;; Loading file read-test-1.lisp ...\n3\n;; Loading of file read-test-1.lisp is finished.\n4\nT\n5\n* (read-test-1)\n6\nnext expression in file: 1\n7\nnext expression in file: 2\n8\nnext expression in file: 3\n9\nnext expression in file: 4\n10\nnext expression in file: \"the cat bit the rat\"\n11\nNIL\nNote: the string “the cat bit the rat” prints as a string (with quotes) because we used a ∼S instead of\na ∼A in the format string in the call to function format.\nIn this last example, we passed the file name as a string to the macro with-open-file. This is not\ngenerally portable across all operating systems. Instead, we could have created a pathname object\nand passed that instead. The pathname function can take eight different keyword arguments, but\nwe will use only the two most common in the example in the file read-test-2.lisp in the src directory.\nThe following listing shows just the differences between this example and the last:\n(let ((a-path-name\n(make-pathname :directory \"testdata\"\n:name \"test.dat\")))\n(with-open-file\n(input-stream a-path-name :direction :input)\nHere, we are specifying that we want to use the file test.dat in the subdirectory testdata. Note: I\nalmost never use pathnames. Instead, I specify files using a string and the character / as a directory\ndelimiter. I find this to be portable for the Macintosh, Windows, and Linux operating systems using\nall Common Lisp implementations.\n\nInput and Output\n60\nThe file readline-test.lisp is identical to the file read-test-1.lisp except that we call function\nreadline instead of the function read and we change the output format message to indicate that\nan entire line of text has been read\n(defun readline-test ()\n\"read a maximum of 1000 expressions from the file 'test.dat'\"\n(with-open-file\n(input-stream \"test.dat\" :direction :input)\n(dotimes (i 1000)\n(let ((x (read-line input-stream nil nil)))\n(if (null x) (return)) ;; break out of the 'dotimes' loop\n(format t \"next line in file: ~S~%\" x)))))\nWhen we execute the expression (readline-test), notice that the string contained in the second line\nof the input file has the quote characters escaped:\n1\n* (load \"readline-test.lisp\")\n2\n;; Loading file readline-test.lisp ...\n3\n;; Loading of file readline-test.lisp is finished.\n4\nT\n5\n* (readline-test)\n6\nnext line in file: \"1 2 3\"\n7\nnext line in file: \"4 \\\"the cat bit the rat\\\"\"\n8\nNIL\n9\n*\nWe can also create an input stream from the contents of a string. The file read-from-string-test.lisp\nis very similar to the example file read-test-1.lisp except that we use the macro with-input-from-\nstring (notice how I escaped the quote characters used inside the test string):\n(defun read-from-string-test ()\n\"read a maximum of 1000 expressions from a string\"\n(let ((str \"1 2 \\\"My parrot is named Brady.\\\" (11 22)\"))\n(with-input-from-string\n(input-stream str)\n(dotimes (i 1000)\n(let ((x (read input-stream nil nil)))\n(if (null x) (return)) ;; break out of the 'dotimes' loop\n(format t \"next expression in string: ~S~%\" x))))))\nWe see the following output when we load the file read-from-string-test.lisp:\n\nInput and Output\n61\n1\n* (load \"read-from-string-test.lisp\")\n2\n;; Loading file read-from-string-test.lisp ...\n3\n;; Loading of file read-from-string-test.lisp is finished.\n4\nT\n5\n* (read-from-string-test)\n6\nnext expression in string: 1\n7\nnext expression in string: 2\n8\nnext expression in string: \"My parrot is named Brady.\"\n9\nnext expression in string: (11 22)\n10\nNIL\n11\n*\nWe have seen how the stream abstraction is useful for allowing the same operations on a variety\nof stream data. In the next section, we will see that this generality also applies to the Lisp printing\nfunctions.\nLisp Printing Functions\nAll of the printing functions that we will look at in this section take an optional last argument that\nis an output stream. The exception is the format function that can take a stream value as its first\nargument (or t to indicate *standard-output*, or a nil value to indicate that format should return\na string value).\nHere is an example of specifying the optional stream argument:\n1\n* (print \"testing\")\n2\n3\n\"testing\"\n4\n\"testing\"\n5\n* (print \"testing\" *standard-output*)\n6\n7\n\"testing\"\n8\n\"testing\"\n9\n*\nThe function print prints Lisp objects so that they can be read back using function read. The\ncorresponding function princ is used to print for “human consumption”. For example:\n\nInput and Output\n62\n1\n* (print \"testing\")\n2\n3\n\"testing\"\n4\n\"testing\"\n5\n* (princ \"testing\")\n6\ntesting\n7\n\"testing\"\n8\n*\nBoth print and princ return their first argument as their return value, which you see in the previous\noutput. Notice that princ also does not print a new line character, so princ is often used with terpri\n(which also takes an optional stream argument).\nWe have also seen many examples in this book of using the format function. Here is a different use\nof format, building a string by specifying the value nil for the first argument:\n1\n* (let ((l1 '(1 2))\n2\n(x 3.14159))\n3\n(format nil \"~A~A\" l1 x))\n4\n\"(1 2)3.14159\"\n5\n*\nWe have not yet seen an example of writing to a file. Here, we will use the with-open-file macro\nwith options to write a file and to delete any existing file with the same name:\n(with-open-file (out-stream \"test1.dat\"\n:direction :output\n:if-exists :supersede)\n(print \"the cat ran down the road\" out-stream)\n(format out-stream \"1 + 2 is: ~A~%\" (+ 1 2))\n(princ \"Stoking!!\" out-stream)\n(terpri out-stream))\nHere is the result of evaluating this expression (i.e., the contents of the newly created file test1.dat\nin the src directory):\n1\n% cat test1.dat\n2\n3\n\"the cat ran down the road\" 1 + 2 is: 3\n4\nStoking!!\nNotice that print generates a new line character before printing its argument.\n\nPlotting Data\nWe will use Zach Beane’s vecto library³¹ for plotting data with the results written to files. Ideally we\nwould like to have interactive plotting capability but for the purposes of this book I need to support\nthe combinations of all Common Lisp implementations on multiple operating systems. Interactive\nplotting libraries are usually implementation and OS dependent. We will use the plotlib example\nwe develop in the later chapter Backpropagation Neural Networks.\nImplementing the Library\nThe examples here are all contained in the directory src/plotlib and is packaged as a Quicklisp\nloadable library. This library will be used in later chapters.\nWhen I work on my macOS laptop, I leave the output graphics file open in the Preview App and\nwhenever I rerun a program producing graphics in the REPL, making the preview App window\nactive refreshes the graphics display.\nPNG file generated by running plotlib test\nThe following listing shows the file plotlib.lisp that is a simple wrapper for the vecto Common Lisp\nplotting library. Please note that I only implemented wrappers for vecto functionality that I need\nfor later examples in this book, so the following code is not particularly general but should be easy\nenough for you to extend for the specific needs of your projects.\n³¹http://xach.com/lisp/vecto/\n\nPlotting Data\n64\n1\n;; Misc. plotting examples using the vecto library\n2\n3\n(ql:quickload :vecto) ;; Zach Beane's plotting library\n4\n(defpackage #:plotlib\n5\n(:use #:cl #:vecto))\n6\n7\n(in-package #:plotlib)\n8\n9\n;; the coordinate (0,0) is the lower left corner of the plotting area.\n10\n;; Increasing the y coordinate is \"up page\" and increasing x is \"to the right\"\n11\n12\n;; fills a rectangle with a gray-scale value\n13\n(defun plot-fill-rect (x y width height gscale) ; 0 < gscale < 1\n14\n(set-rgb-fill gscale gscale gscale)\n15\n(rectangle x y width height)\n16\n(fill-path))\n17\n18\n;; plots a frame rectangle\n19\n(defun plot-frame-rect (x y width height)\n20\n(set-line-width 1)\n21\n(set-rgb-fill 1 1 1)\n22\n(rectangle x y width height)\n23\n(stroke))\n24\n25\n(defun plot-line(x1 y1 x2 y2)\n26\n(set-line-width 1)\n27\n(set-rgb-fill 0 0 0)\n28\n(move-to x1 y1)\n29\n(line-to x2 y2)\n30\n(stroke))\n31\n32\n(defun plot-string(x y str)\n33\n(let ((font (get-font \"OpenSans-Regular.ttf\")))\n34\n(set-font font 15)\n35\n(set-rgb-fill 0 0 0)\n36\n(draw-string x y str)))\n37\n38\n(defun plot-string-bold(x y str)\n39\n(let ((font (get-font \"OpenSans-Bold.ttf\")))\n40\n(set-font font 15)\n41\n(set-rgb-fill 0 0 0)\n42\n(draw-string x y str)))\n43\n\nPlotting Data\n65\n44\n45\n(defun test-plotlib (file)\n46\n(with-canvas (:width 90 :height 90)\n47\n(plot-fill-rect 5 10 15 30 0.2) ; black\n48\n(plot-fill-rect 25 30 30 7 0.7) ; medium gray\n49\n(plot-frame-rect 10 50 30 7)\n50\n(plot-line 90 5 10 5)\n51\n(plot-string 10 65 \"test 1 2 3\")\n52\n(plot-string-bold 10 78 \"Hello\")\n53\n(save-png file)))\n54\n55\n;;(test-plotlib \"test-plotlib.png\")\nThis plot library is used in later examples in the chapters on search, backpropagation neural networks\nand Hopfield neural networks. I prefer using implementation and operating specific plotting libraires\nfor generating interactive plots, but the advantage of writing plot data to a file using the vecto library\nis that the code is portable across operating systems and Common Lisp implementations.\nPackaging as a Quicklisp Project\nThe two files src/plotlib/plotlib.asd src/plotlib/package.lisp configure the library. The file pack-\nage.lisp defines the required library vecto and lists the functions that are publicly exported from\nthe library:\n(defpackage #:plotlib\n(:use #:cl #:vecto)\n(:export save-png plot-fill-rect plot-frame-rect\nplot-size-rect plot-line plot-string plot-string-bold\npen-width))\nTo run the test function provided with this library you load the library and preface exported function\nnames with the package name plotlib: as in this example:\n(ql:quickload \"plotlib\")\n(plotlib::test-plotlib \"test-plotlib.png\")\nIn addition to a package.lisp file we also use a file with the extension .asd\n\nPlotting Data\n66\n(asdf:defsystem #:plotlib\n:description \"Describe plotlib here\"\n:author \"mark.watson@gmail.com\"\n:license \"Apache 2\"\n:depends-on (#:vecto)\n:components ((:file \"package\")\n(:file \"plotlib\")))\nIf you have specified a dependency that is not already downloaded to your computer, Quicklisp will\ninstall the dependency for you.\n\nCommon Lisp Object System - CLOS\nCLOS was the first ANSI standardized object oriented programming facility. While I do not use\nclasses and objects as often in my Common Lisp programs as I do when using Java and Smalltalk,\nit is difficult to imagine a Common Lisp program of any size that did not define and use at least a\nfew CLOS classes.\nThe example program for this chapter in the file src/loving_snippets/HTMLstream.lisp. I used this\nCLOS class about ten years ago in a demo for my commercial natural language processing product\nto automatically generate demo web pages.\nWe are going to start our discussion of CLOS somewhat backwards by first looking at a short test\nfunction that uses the HTMLstream class. Once we see how to use this example CLOS class, we\nwill introduce a small subset of CLOS by discussing in some detail the implementation of the\nHTMLstream class and finally, at the end of the chapter, see a few more CLOS programming\ntechniques. This book only provides a brief introduction to CLOS; the interested reader is encouraged\nto do a web search for “CLOS tutorial”.\nThe macros and functions defined to implement CLOS are a standard part of Common Lisp. Common\nLisp supports generic functions, that is, different functions with the same name that are distinguished\nby different argument types.\nExample of Using a CLOS Class\nThe file src/loving_snippets/HTMLstream.lisp contains a short test program at the end of the file:\n1\n(defun test (&aux x)\n2\n(setq x (make-instance 'HTMLstream))\n3\n(set-header x \"test page\")\n4\n(add-element x \"test text - this could be any element\")\n5\n(add-table\n6\nx\n7\n'((\"<b>Key phrase</b>\" \"<b>Ranking value</b>\")\n8\n(\"this is a test\" 3.3)))\n9\n(get-html-string x))\nThe generic function make-instance takes the following arguments:\n\nCommon Lisp Object System - CLOS\n68\n1\nmake-instance class-name &rest initial-arguments &key ...\nThere are four generic functions used in the function test:\n• set-header - required to initialize class and also defines the page title\n• add-element - used to insert a string that defines any type of HTML element\n• add-table - takes a list of lists and uses the list data to construct an HTML table\n• get-html-string - closes the stream and returns all generated HTML data as a string\nThe first thing to notice in the function test is that the first argument for calling each of these generic\nfunctions is an instance of the class HTMLstream. You are free to also define a function, for example,\nadd-element that does not take an instance of the class HTMLstream as the first function argument\nand calls to add-element will be routed correctly to the correct function definition.\nWe will see that the macro defmethod acts similarly to defun except that it also allows us to define\nmany methods (i.e., functions for a class) with the same function name that are differentiated by\ndifferent argument types and possibly different numbers of arguments.\nImplementation of the HTMLstream Class\nThe class HTMLstream is very simple and will serve as a reasonable introduction to CLOS\nprogramming. Later we will see more complicated class examples that use multiple inheritance.\nStill, this is a good example because the code is simple and the author uses this class frequently\n(some proof that it is useful!). The code fragments listed in this section are all contained in the file\nsrc/loving_snippets/HTMLstream.lisp. We start defining a new class using the macro defclass\nthat takes the following arguments:\n1\ndefclass class-name list-of-super-classes\n2\nlist-of-slot-specifications class-specifications\nThe class definition for HTMLstream is fairly simple:\n1\n(defclass HTMLstream ()\n2\n((out :accessor out))\n3\n(:documentation \"Provide HTML generation services\"))\nHere, the class name is HTMLstream, the list of super classes is an empty list (), the list of slot\nspecifications contains only one slot specification for the slot named out and there is only one class\nspecification: a documentation string. Slots are like instance variables in languages like Java and\nSmalltalk. Most CLOS classes inherit from at least one super class but we will wait until the next\nsection to see examples of inheritance. There is only one slot (or instance variable) and we define\n\nCommon Lisp Object System - CLOS\n69\nan accessor variable with the same name as the slot name. This is a personal preference of mine to\nname read/write accessor variables with the same name as the slot.\nThe method set-header initializes the string output stream used internally by an instance of this\nclass. This method uses convenience macro with-accessors that binds a local set of local variable\nto one or more class slot accessors. We will list the entire method then discuss it:\n1\n(defmethod set-header ((ho HTMLstream) title)\n2\n(with-accessors\n3\n((out out))\n4\nho\n5\n(setf out (make-string-output-stream))\n6\n(princ \"<HTML><head><title>\" out)\n7\n(princ title out)\n8\n(princ \"</title></head><BODY>\" out)\n9\n(terpri out)))\nThe first interesting thing to notice about the defmethod is the argument list: there are two\narguments ho and title but we are constraining the argument ho to be either a member of the\nclass HTMLstream or a subclass of HTMLstream. Now, it makes sense that since we are passing\nan instance of the class HTMLstream to this generic function (or method – I use the terms “generic\nfunction” and “method” interchangeably) that we would want access to the slot defined for this class.\nThe convenience macro with-accessors is exactly what we need to get read and write access to the\nslot inside a generic function (or method) for this class. In the term ((out out)), the first out is local\nvariable bound to the value of the slot named out for this instance ho of class HTMLstream. Inside\nthe with-accessors macro, we can now use setf to set the slot value to a new string output stream.\nNote: we have not covered the Common Lisp type string-output-stream yet in this book, but we\nwill explain its use on the next page.\nBy the time a call to the method set-header (with arguments of an HTMLstream instance and a\nstring title) finishes, the instance has its slot set to a new string-output-stream and HTML header\ninformation is written to the newly created string output stream. Note: this string output stream is\nnow available for use by any class methods called after set-header.\nThere are several methods defined in the file src/loving_snippets/HTMLstream.lisp, but we will\nlook at just four of them: add-H1, add-element, add-table, and get-html-string. The remaining\nmethods are very similar to add-H1 and the reader can read the code in the source file.\nAs in the method set-header, the method add-H1 uses the macro with-accessors to access the stream\noutput stream slot as a local variable out. In add-H1 we use the function princ that we discussed in\nChapter on Input and Output to write HTML text to the string output stream:\n\nCommon Lisp Object System - CLOS\n70\n1\n(defmethod add-H1 ((ho HTMLstream) some-text)\n2\n(with-accessors\n3\n((out out))\n4\nho\n5\n(princ \"<H1>\" out)\n6\n(princ some-text out)\n7\n(princ \"</H1>\" out)\n8\n(terpri out)))\nThe method add-element is very similar to add-H1 except the string passed as the second argument\nelement is written directly to the stream output stream slot:\n1\n(defmethod add-element ((ho HTMLstream) element)\n2\n(with-accessors\n3\n((out out))\n4\nho\n5\n(princ element out)\n6\n(terpri out)))\nThe method add-table converts a list of lists into an HTML table. The Common Lisp function princ-\nto-string is a useful utility function for writing the value of any variable to a string. The functions\nstring-left-trim and string-right-trim are string utility functions that take two arguments: a list of\ncharacters and a string and respectively remove these characters from either the left or right side of\na string. Note: another similar function that takes the same arguments is string-trim that removes\ncharacters from both the front (left) and end (right) of a string. All three of these functions do not\nmodify the second string argument; they return a new string value. Here is the definition of the\nadd-table method:\n1\n(defmethod add-table ((ho HTMLstream) table-data)\n2\n(with-accessors\n3\n((out out))\n4\nho\n5\n(princ \"<TABLE BORDER=\\\"1\\\" WIDTH=\\\"100\\%\\\">\" out)\n6\n(dolist (d table-data)\n7\n(terpri out)\n8\n(princ \"\n<TR>\" out)\n9\n(terpri out)\n10\n(dolist (w d)\n11\n(princ \"\n<TD>\" out)\n12\n(let ((str (princ-to-string w)))\n13\n(setq str (string-left-trim '(#\\() str))\n14\n(setq str (string-right-trim '(#\\)) str))\n\nCommon Lisp Object System - CLOS\n71\n15\n(princ str out))\n16\n(princ \"</TD>\" out)\n17\n(terpri out))\n18\n(princ \"\n</TR>\" out)\n19\n(terpri out))\n20\n(princ \"</TABLE>\" out)\n21\n(terpri out)))\nThe method get-html-string gets the string stored in the string output stream slot by using the\nfunction get-output-stream-string:\n1\n(defmethod get-html-string ((ho HTMLstream))\n2\n(with-accessors\n3\n((out out))\n4\nho\n5\n(princ \"</BODY></HTML>\" out)\n6\n(terpri out)\n7\n(get-output-stream-string out)))\nCLOS is a rich framework for object oriented programming, providing a superset of features found\nin languages like Java, Ruby, and Smalltalk. I have barely scratched the surface in this short CLOS\nexample for generating HTML. Later in the book, whenever you see calls to make-instance, that\nlets you know we are using CLOS even if I don’t specifically mention CLOS in the examples.\nUsing Defstruct or CLOS\nYou might notice from my own code that I use Common Lisp defstruct macros to define data\nstructures more often than I use CLOS. The defclass macro used to create CLOS classes are much\nmore flexible but for simple data structures I find that using defstruct is much more concise. In the\nsimplest case, a defstruct can just be a name of the new type followed by slot names. For each slot\nlike my-slot-1 accessor functions are generated automatically. Here is a simple example:\n1\n$ ccl\n2\nClozure Common Lisp Version 1.12\nDarwinX8664\n3\n? (defstruct struct1 s1 s2)\n4\nSTRUCT1\n5\n? (make-struct1 :s1 1 :s2 2)\n6\n#S(STRUCT1 :S1 1 :S2 2)\n7\n? (struct1-s1 (make-struct1 :s1 1 :s2 2))\n8\n1\n\nCommon Lisp Object System - CLOS\n72\nWe defined a struct struct1 on line3 with two slots names s1 and s2, show the use of the automatically\ngenerated constructor make-struct1 on line 5, and one of the two automatically generated accessor\nfunctions struct1-s1 on line 7. The names of accessor functions are formed with the structure name\nand the slot name.\n\nHeuristically Guided Search\nWe represent search space as a graph: nodes and links between the nodes. The following figure\nshows the simple graph that we use as an example, finding a route from node n1 to node n11:\nPlot of best route using the plotlib utilities\nThe following example code uses a heuristic for determining which node to try first from any specific\nlocation: move to the node that is closest spatially to the goal node. We see that this heuristic will\nnot always work to produce the most efficient search but we will still get to the goal node. As an\nexample in which the heuristic does not work, consider when we start at node n1 in the lower left\ncorner of the figure. The search algorithm can add nodes n2 and n4 to the nodes to search list and\nwill search using node n4 first since n4 is closer to the goal node n11 than node n2. In this case,\nthe search will eventually need to back up trying the path n1 to n2. Despite this example of the\nheuristic not working to decrease search time, in general, for large search spaces (i.e., graphs with\nmany nodes and edges) it can dramatically decrease search time.\nThe main function A*search starting in line 5 extends to line 151 because all search utility functions\nare nested (lexically scoped) inside the mani function. The actual code for the main function\nA*search is in lines 150 and 151.\n\nHeuristically Guided Search\n74\nThe data representing nodes in this implementation is globally scoped (see the definitions on lines\n155-165 in the “throw away test code” near the bottom of the file) and we set the property path-list\nto store the nodes directy connected to each node (set in function init-path-list in lines 36-52). I\noriginally wrote this code in 1990 which explains it non-functional style using globally scoped node\nvariables.\n1\n;; Perform a heuristic A* search between the start and goal nodes:\n2\n;;\n3\n;; Copyright 1990, 2017 by Mark Watson\n4\n5\n(defun A*search (nodes paths start goal &aux possible-paths best)\n6\n7\n(defun Y-coord (x) (truncate (cadr x)))\n8\n(defun X-coord (x) (truncate (car x)))\n9\n10\n(defun dist-between-points (point1 point2)\n11\n(let ((x-dif (- (X-coord point2) (X-coord point1)))\n12\n(y-dif (- (Y-coord point2) (Y-coord point1))))\n13\n(sqrt (+ (* x-dif x-dif)\n(* y-dif y-dif)))))\n14\n15\n(setq possible-paths\n16\n(list\n17\n(list\n18\n(dist-between-points\n19\n(eval start)\n20\n(eval goal))\n21\n0\n22\n(list start))))\n23\n24\n(defun init-network ()\n25\n(setq paths (init-lengths paths))\n26\n(init-path-list nodes paths))\n27\n28\n(defun init-lengths (pathlist)\n29\n(let (new-path-list pathlength path-with-length)\n30\n(dolist (path pathlist)\n31\n(setq pathlength (slow-path-length path))\n32\n(setq path-with-length (append path (list pathlength)))\n33\n(setq new-path-list (cons path-with-length new-path-list)))\n34\nnew-path-list))\n35\n36\n(defun init-path-list (nodes paths)\n37\n(dolist (node nodes)\n\nHeuristically Guided Search\n75\n38\n(setf\n39\n(get node 'path-list)\n40\n;; let returns all paths connected to node:\n41\n(let (path-list)\n42\n(dolist (path paths)\n43\n(if (equal node (start-node-name path))\n44\n(setq path-list\n45\n(cons (list (end-node-name path)\n46\n(path-length path))\n47\npath-list))\n48\n(if (equal node (end-node-name path))\n49\n(setq path-list (cons (list (start-node-name path)\n50\n(path-length path))\n51\npath-list)))))\n52\npath-list ))))\n53\n54\n(defun slow-path-length (path)\n55\n(dist-between-points (start-node path) (end-node path)))\n56\n57\n(defun path-length (x) (caddr x))\n58\n59\n(defun start-node (path) (eval (car path)))\n60\n(defun end-node (path) (eval (cadr path)))\n61\n(defun start-node-name (x) (car x))\n62\n(defun end-node-name (x) (cadr x))\n63\n(defun first-on-path (x) (caddr x))\n64\n(defun goal-node (x) (car x))\n65\n(defun distance-to-that-node (x) (cadr x))\n66\n67\n(defun enumerate-children (node goal)\n68\n(let* ((start-to-lead-node-dist (cadr node)) ;; distance already calculated\n69\n(path (caddr node))\n70\n(lead-node (car path)))\n71\n(if (get-stored-path lead-node goal)\n72\n(consider-best-path lead-node goal path start-to-lead-node-dist)\n73\n(consider-all-nodes lead-node goal path start-to-lead-node-dist))))\n74\n75\n(defun consider-best-path (lead-node goal path distance-to-here)\n76\n(let* (\n77\n(first-node (get-first-node-in-path lead-node goal))\n78\n(dist-to-first (+ distance-to-here\n79\n(get-stored-dist lead-node first-node)))\n80\n(total-estimate (+ distance-to-here\n\nHeuristically Guided Search\n76\n81\n(get-stored-dist lead-node goal)))\n82\n(new-path (cons first-node path)))\n83\n(list (list total-estimate dist-to-first new-path))))\n84\n85\n(defun get-stored-path (start goal)\n86\n(if (equal start goal)\n87\n(list start 0)\n88\n(assoc goal (get start 'path-list))))\n89\n90\n(defun node-not-in-path (node path)\n91\n(if (null path)\n92\nt\n93\n(if (equal node (car path))\n94\nnil\n95\n(node-not-in-path node (cdr path)))))\n96\n97\n(defun consider-all-nodes (lead-node goal path start-to-lead-node-dist)\n98\n(let (dist-to-first total-estimate new-path new-nodes)\n99\n(dolist (node (collect-linked-nodes lead-node))\n100\n(if (node-not-in-path node path)\n101\n(let ()\n102\n(setq dist-to-first (+ start-to-lead-node-dist\n103\n(get-stored-dist lead-node node)))\n104\n(setq total-estimate (+ dist-to-first\n105\n(dist-between-points\n106\n(eval node)\n107\n(eval\ngoal))))\n108\n(setq new-path (cons node path))\n109\n(setq new-nodes (cons (list total-estimate\n110\ndist-to-first\n111\nnew-path)\n112\nnew-nodes)))))\n113\nnew-nodes))\n114\n115\n(defun collect-linked-nodes (node)\n116\n(let (links)\n117\n(dolist (link (get node 'path-list))\n118\n(if (null (first-on-path link))\n119\n(setq links (cons (goal-node link) links))))\n120\nlinks))\n121\n122\n(defun get-stored-dist (node1 node2)\n123\n(distance-to-that-node (get-stored-path node1 node2)))\n\nHeuristically Guided Search\n77\n124\n125\n(defun collect-ascending-search-list-order (a l)\n126\n(if (null l)\n127\n(list a)\n128\n(if (< (car a) (caar l))\n129\n(cons a l)\n130\n(cons (car l) (Collect-ascending-search-list-order a (cdr l))))))\n131\n132\n(defun get-first-node-in-path (start goal)\n133\n(let (first-node)\n134\n(setq first-node\n(first-on-path (get-stored-path start goal)))\n135\n(if first-node first-node goal)))\n136\n137\n(defun a*-helper ()\n138\n(if possible-paths\n139\n(let ()\n140\n(setq best (car possible-paths))\n141\n(setq possible-paths (cdr possible-paths))\n142\n(if (equal (first (caddr best)) goal)\n143\nbest\n144\n(let ()\n145\n(dolist (child (enumerate-children best goal))\n146\n(setq possible-paths\n147\n(collect-ascending-search-list-order\n148\nchild possible-paths)))\n149\n(a*-helper))))))\n150\n(init-network)\n151\n(reverse (caddr (a*-helper))))\n152\n153\n;;\nThrow away test code:\n154\n155\n(defvar n1 '(30 201))\n156\n(defvar\nn2 '(25 140))\n157\n(defvar\nn3 '(55 30))\n158\n(defvar\nn4 '(105 190))\n159\n(defvar\nn5 '(95 110))\n160\n(defvar\nn6 '(140 22))\n161\n(defvar\nn7 '(160 150))\n162\n(defvar\nn8 '(170 202))\n163\n(defvar\nn9 '(189 130))\n164\n(defvar\nn10 '(200 55))\n165\n(defvar\nn11 '(205 201))\n166\n\nHeuristically Guided Search\n78\n167\n(print (A*search\n168\n'(n1 n2 n3 n4 n5 n6 n7 n8 n9 n10 n11) ;; nodes\n169\n'((n1 n2) (n2 n3) (n3 n5) (n3 n6) (n6 n10) ;; paths\n170\n(n9 n10) (n7 n9) (n1 n4) (n4 n2) (n5 n8)\n171\n(n8 n4) (n7 n11))\n172\n'n1 'n11)) ;; starting and goal nodes\nThe following example in the repl shows the calculation of the path that we saw in the figure of the\ngraph search space.\n1\n$ sbcl\n2\n* (load \"astar_search.lisp\")\n3\n4\n(N1 N2 N3 N6 N10 N9 N7 N11)\n5\nT\n6\n*\nThere are many types of search: breadth first as we used here, depth first, with heuristics to optimize\nsearch dependent on the type of search space.\n\nNetwork Programming\nDistributed computing is pervasive: you need to look no further than the World Wide Web,\nInternet chat, etc. Of course, as a Lisp programmer, you will want to do at least some of your\nnetwork programming in Lisp! The previous editions of this book provided low level socket network\nprogramming examples. I decided that for this new edition, I would remove those examples and\ninstead encourage you to “move further up the food chain” and work at a higher level of abstraction\nthat makes sense for the projects you will likely be developing. Starting in the 1980s, a lot of my\nwork entailed low level socket programming for distributed networked applications. As I write this,\nit is 2013, and there are better ways to structure distributed applications.\nSpecifically, since many of the examples later in this book fetch information from the web and linked\ndata sources, we will start be learning how to use Edi Weitz’s Drakma HTTP client library³². In order\nto have a complete client server example we will also look briefly at Edi Weitz’s Hunchentoot web\nserver³³ that uses JSON as a data serialization format. I used to use XML for data serialization but\nJSON has many advantages: easier for a human to read and it plays nicely with Javascript code and\nsome data stores like Postgres (new in versions 9.x), MongoDB, and CouchDB that support JSON as\na native data format.\nThe code snippets in the first two sections of this chapter are derived from examples in the Drackma\nand Hunchentoot documentation.\nAn introduction to Drakma\nEdi Weitz’s Drakma library³⁴ supports fetching data via HTTP requests. As you can see in the\nDrakma documentation, you can use this library for authenticated HTTP requests (i.e., allow you\nto access web sites that require a login), support HTTP GET and PUT operations, and deal with\ncookies. The top level API that we will use is drakma:http-request that returns multiple values. In\nthe following example, I want only the first three values, and ignore the others like the original URI\nthat was fetched and an IO stream object. We use the built-in Common Lisp macro multiple-value-\nsetq:\n³²http://weitz.de/drakma/\n³³http://weitz.de/hunchentoot/\n³⁴http://weitz.de/drakma/\n\nNetwork Programming\n80\n1\n* (ql:quickload :drakma)\n2\n* (multiple-value-setq\n3\n(data http-response-code headers)\n4\n(drakma:http-request \"http://markwatson.com\"))\nI manually formatted the last statement I entered in the last repl listing and I will continue to\nmanually edit the repl listings in the rest of this book to make them more easily readable.\nThe following shows some of the data bound to the variables data, http-response-code, and\nheaders:\n1\n* data\n2\n3\n\"<!DOCTYPE html>\n4\n<html>\n5\n<head>\n6\n<title>Mark Watson: Consultant and Author</title>\nThe value of http-response-code is 200 which means that there were no errors:\n1\n* http-response-code\n2\n3\n200\nThe HTTP response headers will be useful in many applications; for fetching the home page of my\nweb site the headers are:\n1\n* headers\n2\n3\n((:SERVER . \"nginx/1.1.19\")\n4\n(:DATE . \"Fri, 05 Jul 2013 15:18:27 GMT\")\n5\n(:CONTENT-TYPE . \"text/html; charset=utf-8\")\n6\n(:TRANSFER-ENCODING . \"chunked\")\n7\n(:CONNECTION . \"close\")\n8\n(:SET-COOKIE\n9\n.\n10\n\"ring-session=cec5d7ba-e4da-4bf4-b05e-aff670e0dd10;Path=/\"))\nWe will use Drakma later in this book for several examples. In the next section we will write a web\napp using Hunchentoot and test it with a Drakma client.\n\nNetwork Programming\n81\nAn introduction to Hunchentoot\nEdi Weitz’s Hunchentoot project³⁵ is a flexible library for writing web applications and web services.\nWe will also use Edi’s CL-WHO library in this section for generating HTML from Lisp code.\nHunchentoot will be installed the first time you quick load it in the example code for this section:\n1\n(ql:quickload \"hunchentoot\")\nI will use only easy handler framework³⁶ in the Hunchentoot examples in this section. I leave it to\nyou to read the documentation on using custom acceptors³⁷ after you experiment with the examples\nin this section.\nThe following code will work for both multi-threading installations of SBCL and single thread\ninstallations (e.g., some default installations of SBCL on OS X):\n1\n(ql:quickload :hunchentoot)\n2\n(ql:quickload :cl-who)\n3\n4\n(in-package :cl-user)\n5\n(defpackage hdemo\n6\n(:use :cl\n7\n:cl-who\n8\n:hunchentoot))\n9\n(in-package :hdemo)\n10\n11\n(defvar *h* (make-instance 'easy-acceptor :port 3000))\n12\n13\n;; define a handler with the arbitrary name my-greetings:\n14\n15\n(define-easy-handler (my-greetings :uri \"/hello\") (name)\n16\n(setf (hunchentoot:content-type*) \"text/html\")\n17\n(with-html-output-to-string (*standard-output* nil :prologue t)\n18\n(:html\n19\n(:head (:title \"hunchentoot test\"))\n20\n(:body\n21\n(:h1 \"hunchentoot form demo\")\n22\n(:form\n23\n:method :post\n24\n(:input :type :text\n25\n:name \"name\"\n³⁵http://weitz.de/hunchentoot/\n³⁶http://weitz.de/hunchentoot/#easy-handlers\n³⁷http://weitz.de/hunchentoot/#acceptors\n\nNetwork Programming\n82\n26\n:value name)\n27\n(:input :type :submit :value \"Submit your name\"))\n28\n(:p \"Hello \" (str name))))))\n29\n30\n(hunchentoot:start *h*)\nIn lines 5 through 9 we create an use a new package that includes support for generating HTML\nin Lisp code (CL-WHO) and the Hunchentoot library). On line 11 we create an instance of an easy\nacceptor on port 3000 that provides useful default behaviors for providing HTTP services.\nThe Hunchentoot macro define-easy-handler is used in lines 15 through 28 to define an HTTP\nrequest handler and add it to the easy acceptor instance. The first argument, my-greetings in this\nexample, is an arbitrary name and the keyword :uri argument provides a URL pattern that the easy\nacceptor server object uses to route requests to this handler. For example, when you run this example\non your computer, this URL routing pattern would handle requests like:\n1\nhttp://localhost:3000/hello\nIn lines 17 through 28 we are using the CL-WHO library to generate HTML for a web page. As you\nmight guess, :html generates the outer <html></html> tags for a web page. Line 19 would generate\nHTML like:\n1\n<head>\n2\n<title>hunchentoot test</title>\n3\n</head>\nLines 22 through 27 generate an HTML input form and line 28 displays any value generated when\nthe user entered text in the input filed and clicked the submit button. Notice the definition of the\nargument name in line 1 in the definition of the easy handler. If the argument name is not defined,\nthe nil value will be displayed in line 28 as an empty string.\nYou should run this example and access the generated web page in a web browser, and enter text,\nsubmit, etc. You can also fetch the generated page HTML using the Drakma library that we saw in\nthe last section. Here is a code snippet using the Drakma client library to access this last example:\n\nNetwork Programming\n83\n1\n* (drakma:http-request \"http://127.0.0.1:3000/hello?name=Mark\")\n2\n3\n\"Hello Mark\"\n4\n200\n5\n((:CONTENT-LENGTH . \"10\")\n6\n(:DATE . \"Fri, 05 Jul 2013 15:57:22 GMT\")\n7\n(:SERVER . \"Hunchentoot 1.2.18\")\n8\n(:CONNECTION . \"Close\")\n9\n(:CONTENT-TYPE . \"text/plain; charset=utf-8\"))\n10\n#<PURI:URI http://127.0.0.1:3000/hello?name=Mark>\n11\n#<FLEXI-STREAMS:FLEXI-IO-STREAM {10095654A3}>\n12\nT\n13\n\"OK\"\nWe will use both Drackma and Hunchentoot in the next section.\nComplete REST Client Server Example Using JSON for\nData Serialization\nA reasonable way to build modern distributed systems is to write REST web services that serve JSON\ndata to client applications. These client applications might be rich web apps written in Javascript,\nother web services, and applications running on smartphones that fetch and save data to a remote\nweb service.\nWe will use the cl-json Quicklisp package to encode Lisp data into a string representing JSON\nencoded data. Here is a quick example:\n1\n* (ql:quickload :cl-json)\n2\n* (defvar y (list (list '(cat . \"the cat ran\") '(dog . 101)) 1 2 3 4 5))\n3\n4\nY\n5\n* y\n6\n7\n(((CAT . \"the cat ran\") (DOG . 101)) 1 2 3 4 5)\n8\n* (json:encode-json-to-string y)\n9\n\"[{\\\"cat\\\":\\\"the cat ran\\\",\\\"dog\\\":101},1,2,3,4,5]\"\nThe following list shows the contents of the file src/web-hunchentoot-json.lisp:\n\nNetwork Programming\n84\n1\n(ql:quickload :hunchentoot)\n2\n(ql:quickload :cl-json)\n3\n4\n(defvar *h* (make-instance 'hunchentoot:easy-acceptor :port 3000))\n5\n6\n;; define a handler with the name animal:\n7\n8\n(hunchentoot:define-easy-handler (animal :uri \"/animal\") (name)\n9\n(print name)\n10\n(setf (hunchentoot:content-type*) \"text/plain\")\n11\n(cond\n12\n((string-equal name \"cat\")\n13\n(json:encode-json-to-string\n14\n(list\n15\n(list\n16\n'(average_weight . 10)\n17\n'(friendly . nil))\n18\n\"A cat can live indoors or outdoors.\")))\n19\n((string-equal name \"dog\")\n20\n(json:encode-json-to-string\n21\n(list\n22\n(list\n23\n'(average_weight . 40)\n24\n'(friendly . t))\n25\n\"A dog is a loyal creature, much valued by humans.\")))\n26\n(t\n27\n(json:encode-json-to-string\n28\n(list\n29\n()\n30\n\"unknown type of animal\")))))\n31\n32\n(hunchentoot:start *h*)\nThis example is very similar to the web application example in the last section. The difference is\nthat this application is not intended to be viewed on a web page because it returns JSON data as\nHTTP responses. The easy handler definition on line 8 specifies a handler argument name. In lines\n12 and 19 we check to see if the value of the argument name is “cat” or “dog” and if it is, we return\nthe appropriate JSON example data for those animals. If there is no match, the default cond clause\nstarting on line 26 returns a warning string as a JSON encoded string.\nWhile running this test service, in one repl, you can ue the Drakma library in another repl to test it\n(not all output is shown in the next listing):\n\nNetwork Programming\n85\n1\n* (ql:quickload :drakma)\n2\n* (drakma:http-request \"http://127.0.0.1:3000/animal?name=dog\")\n3\n4\n\"[{\\\"average_weight\\\":40,\n5\n\\\"friendly\\\":true},\n6\n\\\"A dog is a loyal creature, much valued by humans.\\\"]\"\n7\n200\n8\n* (drakma:http-request \"http://127.0.0.1:3000/animal?name=cat\")\n9\n10\n\"[{\\\"average_weight\\\":10,\n11\n\\\"friendly\\\":null},\n12\n\\\"A cat can live indoors or outdoors.\\\"]\"\n13\n200\nYou can use the cl-json library to decode a string containing JSON data to Lisp data:\n1\n* (ql:quickload :cl-json)\n2\nTo load \"cl-json\":\n3\nLoad 1 ASDF system:\n4\ncl-json\n5\n; Loading \"cl-json\"\n6\n.\n7\n(:CL-JSON)\n8\n* (cl-json:decode-json-from-string\n9\n(drakma:http-request \"http://127.0.0.1:3000/animal?name=dog\"))\n10\n11\n(((:AVERAGE--WEIGHT . 40) (:FRIENDLY . T))\n12\n\"A dog is a loyal creature, much valued by humans.\")\nFor most of my work, REST web services are “read-only” in the sense that clients don’t modify state\non the server. However, there are use cases where a client application might want to; for example,\nletting clients add new animals to the last example.\n1\n(defparameter *animal-hash* (make-hash-table))\n2\n3\n;; handle HTTP POST requests:\n4\n(hunchentoot:define-easy-handler (some-handler :uri \"/add\") (json-data)\n5\n(setf (hunchentoot:content-type*) \"text/plain\")\n6\n(let* ((data-string (hunchentoot:raw-post-data :force-text t))\n7\n(data (cl-json:decode-json-from-string json-data))\n8\n;; assume that the name of the animal is a hashed value:\n9\n(animal-name (gethash \"name\" data)))\n\nNetwork Programming\n86\n10\n(setf (gethash animal-name *animal-hash*) data))\n11\n\"OK\")\nIn line 4 we are defining an additional easy handler with a handler argument json-data. This data\nis assumed to be a string encoding of JSON data which is decoded into Lisp data in lines 6 and 7. We\nsave the data to the global variable animal-hash.\nIn this example, we are storing data sent from a client in an in-memory hash table. In a real\napplication new data might be stored in a database.\nNetwork Programming Wrap Up\nYou have learned the basics for writing web services and writing clients to use web services. Later,\nwe will use web services written in Python by writing Common Lisp clients: we will wrap retrained\ndeep learning models and access them from Common Lisp.\n\nUsing the Microsoft Bing Search APIs\nI have used the Bing search APIs for many years. Microsoft Bing supports several commercial search\nengine services, including my favorite search engine Duck Duck Go. Bing is now part of the Azure\ninfrastructure that is branded as “Cognitive Services.” You should find the example code for this\nchapter relatively easy to extend to other Azure Cognitive Services that you might need to use.\nYou will need to register with Microsoft’s Azure search service to use the material in this chapter. It\nis likely that you view search as a manual human-centered activity. I hope to expand your thinking\nto considering applications that automate search, finding information on the web, and automatically\norganizing information.\nWhile the example code uses only the search APIs, with some modification it can be extended to\nwork with all REST APIs provided by Azure Cognitive Services³⁸ that include: analyzing text to\nget user intent, general language understanding, detecting key phrases and entity names, translate\nbetween languages, converting between speech and text, and various computer vision services.\nThese services are generally free or very low cost for a few thousand API calls a month, with\nincreased cost for production deployments. Microsoft spends about $1 billion a year in research\nand development for Azure Cognitive Services.\nGetting an Access Key for Microsoft Bing Search APIs\nYou will need to set up an Azure account if you don’t already have one. I use the Bing search APIs\nfairly often for research but I have never spent more than about a dollar a month and usually I get\nno bill at all. For personal use it is a very inexpensive service.\nYou start by going to the web page https://azure.microsoft.com/en-us/try/cognitive-services/³⁹ and\nsign up for an access key. The Search APIs sign up is currently in the fourth tab in this web form.\nWhen you navigate to the Search APIs tab, select the option Bing Search APIs v7. You will get an\nAPI key that you need to store in an environment variable that you will soon need:\nexport BING_SEARCH_V7_SUBSCRIPTION_KEY=1e97834341d2291191c772b7371ad5b7\nThat is not my real subscription key!\nYou also set the Bing search API as an environment variable:\n³⁸https://azure.microsoft.com/en-us/services/cognitive-services/\n³⁹https://azure.microsoft.com/en-us/try/cognitive-services/\n\nUsing the Microsoft Bing Search APIs\n88\nexport BING_SEARCH_V7_ENDPOINT=https://api.cognitive.microsoft.com/bing/v7.0/search\nExample Search Script\nInstead of using a pure Common Lisp HTTP client library I often prefer using the curl command run\nin a separate process. The curl utility handles all possible authentication modes, handles headers,\nresponse data in several formats, etc. We capture the output from curl in a string that in turn gets\nprocessed by a JSON library.\nIt takes very little Common Lisp code to access the Bing search APIs. The function websearch makes\na generic web search query. The function get-wikidata-uri uses the websearch function by adding\n“site:wikidata.org” to the query and returning only the WikiData URI for the original search term.\nWe will later see several examples. I will list the entire library with comments to follow:\n1\n(in-package #:bing)\n2\n3\n(defun get-wikidata-uri (query)\n4\n(let ((sr (websearch (concatenate 'string \"site:wikidata.org \" query))))\n5\n(cadar sr)))\n6\n7\n(defun websearch (query)\n8\n(let* ((key (uiop:getenv \"BING_SEARCH_V7_SUBSCRIPTION_KEY\"))\n9\n(endpoint (uiop:getenv \"BING_SEARCH_V7_ENDPOINT\"))\n10\n(command\n11\n(concatenate\n12\n'string\n13\n\"curl -v -X GET \\\"\"\nendpoint \"?q=\"\n14\n(drakma:url-encode query :utf-8)\n15\n\"&mkt=en-US&limit=4\\\"\"\n16\n\" -H \\\"Ocp-Apim-Subscription-Key: \" key \"\\\"\"))\n17\n(response\n18\n(uiop:run-program command :output :string)))\n19\n(with-input-from-string\n20\n(s response)\n21\n(let* ((json-as-list (json:decode-json s))\n22\n(values (cdadr (cddr (nth 2 json-as-list)))))\n23\n(mapcar #'(lambda (x)\n24\n(let ((name (assoc :name x))\n25\n(display-uri (assoc :display-url x))\n26\n(snippet (assoc :snippet x)))\n27\n(list (cdr name) (cdr display-uri) (cdr snippet))))\n28\nvalues)))))\n\nUsing the Microsoft Bing Search APIs\n89\nWe get the Bing access key and the search API endpoint in lines 8-9. Lines 10-16 create a complete\ncall to the curl* command line utility. We spawn a process to run **curl and capture the string\noutput in the variable response in lines 17-18. You might want to add a few print statements to see\ntypical values for the variables command and response. The response data is JSON data encoded\nin a string, with straightforward code in lines 19-28 to parse out the values we want.\nThe following repl listing shows this library in use:\n$ sbcl\nThis is SBCL 2.0.2, an implementation of ANSI Common Lisp.\n* (ql:quickload \"bing\")\nTo load \"bing\":\nLoad 1 ASDF system:\nbing\n; Loading \"bing\"\n..............\n(\"bing\")\n* (bing:get-wikidata-uri \"Sedona Arizona\")\n\"https://www.wikidata.org/wiki/Q80041\"\n* (bing:websearch \"Berlin\")\n((\"Berlin - Wikipedia\" \"https://en.wikipedia.org/wiki/Berlin\"\n\"Berlin (/ b￿￿r￿l￿n /; German: [b￿￿￿li￿n] (listen)) is the capital and largest cit\\\ny of Germany by both area and population. Its 3,769,495 (2019) inhabitants make it t\\\nhe most populous city proper of the European Union. The city is one of Germany's 16 \\\nfederal states.\")\n(\"THE 15 BEST Things to Do in Berlin - 2020 (with Photos ...\"\n\"https://www.tripadvisor.com/Attractions-g187323-Activities-Berlin.html\"\n\"Book your tickets online for the top things to do in Berlin, Germany on Tripadvis\\\nor: See 571,599 traveler reviews and photos of Berlin tourist attractions. Find what\\\nto do today, this weekend, or in August. We have reviews of the best places to see \\\nin Berlin. Visit top-rated & must-see attractions.\")\n(\"Berlin - Official Website of the City of Berlin, Capital ...\"\n\"https://www.berlin.de/en\"\n\"Official Website of Berlin: Information about the Administration, Events, Culture\\\n, Tourism, Hotels and Hotel Booking, Entertainment, Tickets, Public Transport, Polit\\\nical System, Local Authorities and Business in Berlin.\")\n(\"Berlin | History, Map, Population, Attractions, & Facts ...\"\n\"https://www.britannica.com/place/Berlin\"\n\"Berlin is situated about 112 miles (180 km) south of the Baltic Sea, 118 miles (1\\\n90 km) north of the Czech-German border, 110 miles (177 km) east of the former inner\\\n-German border, and 55 miles (89 km) west of Poland. It lies in the wide glacial val\\\nley of the Spree River, which runs through the centre of the city.\")\n(\"Berlin travel | Germany - Lonely Planet\"\n\nUsing the Microsoft Bing Search APIs\n90\n\"https://www.lonelyplanet.com/germany/berlin\"\n\"Welcome to Berlin Berlin's combo of glamour and grit is bound to mesmerise all th\\\nose keen to explore its vibrant culture, cutting-edge architecture, fabulous food, i\\\nntense parties and tangible history.\")\n(\"Berlin 2020: Best of Berlin, Germany Tourism - Tripadvisor\"\n\"https://www.tripadvisor.com/Tourism-g187323\"\n\"Berlin is an edgy city, from its fashion to its architecture to its charged polit\\\nical history. The Berlin Wall is a sobering reminder of the hyper-charged postwar at\\\nmosphere, and yet the graffiti art that now covers its remnants has become symbolic \\\nof social progress.\")\n(\"Berlin 2020: Best of Berlin, OH Tourism - Tripadvisor\"\n\"https://www.tripadvisor.com/Tourism-g50087-Berlin_Ohio-Vacations.html\"\n\"Berlin Tourism: Tripadvisor has 11,137 reviews of Berlin Hotels, Attractions, and\\\nRestaurants making it your best Berlin resource.\")\n(\"Berlin (band) - Wikipedia\" \"https://en.wikipedia.org/wiki/Berlin_(band)\"\n\"Berlin is the alias for vocalist Terri Nunn, as well as the American new wave ban\\\nd she fronts, having been originally formed in Orange County, California. The band g\\\nained mainstream-commercial success with singles including \\\" Sex (I'm A...) \\\", \\\" \\\nNo More Words \\\" and the chart-topping \\\" Take My Breath Away \\\" from the 1986 film \\\nTop Gun.\")\n(\"Berlin's official travel website - visitBerlin.de\"\n\"https://www.visitberlin.de/en\"\n\"Berlin's way to a metropolis 100 Years of Greater Berlin In 1920, modern Berlin w\\\nas born at one fell swoop. 8 cities, 59 rural communities and 27 manor districts uni\\\nte to form \\\"Greater Berlin\\\"\"))\n*\nI have been using the Bing search APIs for many years. They are a standard part of my application\nbuilding toolkit.\nWrap-up\nYou can check out the wide range of Congitive Services⁴⁰ on the Azure site. Available APIs include:\nlanguage detection, speech recognition, vision libraries for object recognition, web search, and\nanomaly detection in data.\nIn addition to using automated web scraping to get data for my personal research, I often use\nautomated web search. I find the Microsoft’s Azure Bing search APIs are the most convenient to use\nand I like paying for services that I use.\n⁴⁰https://azure.microsoft.com/en-us/try/cognitive-services/\n\nAccessing Relational Databases\nThere are good options for accessing relational databases from Common Lisp. Personally I almost\nalways use Postgres and in the past I used either native foreign client libraries or the socket interface\nto Postgres. Recently, I decided to switch to CLSQL⁴¹ which provides a common interface for\naccessing Postgres, MySQL, SQLite, and Oracle databases. There are also several recent forks of\nCLSQL on github. We will use CLSQL in examples in this book. Hopefully while reading the Chapter\non Quicklisp you installed CLSQL and the back end for one or more databases that you use for your\nprojects.\nFor some database applications when I know that I will always use the embedded SQLite database\n(i.e., that I will never want to switch to Postgres of another database) I will just use the sqlite library\nas I do in chapter Knowledge Graph Navigator.\nIf you have not installed CLSQL yet, then please install it now:\n(ql:quickload \"clsql\")\nYou also need to install one or more CLSQL backends, depending on which relational databases you\nuse:\n(ql:quickload \"clsql-postgresql\")\n(ql:quickload \"clsql-mysql\")\n(ql:quickload \"clsql-sqlite3\")\nThe directory src/clsql_examples contains the standalone example files for this chapter.\nWhile I often prefer hand crafting SQL queries, there seems to be a general movement in software\ndevelopment towards the data mapper or active record design patterns. CLSQL provides Object\nRelational Mapping (ORM) functionality to CLOS.\nYou will need to create a new database news in order to follow along with the examples in this\nchapter and later in this book. I will use Postgres for examples in this chapter and use the following\nto create a new database (my account is “markw” and the following assumes that I have Postgres\nconfigured to not require a password for this account when accessing the database from “localhost”):\n⁴¹http://clsql.b9.com/\n\nAccessing Relational Databases\n92\n1\n->\n~\npsql\n2\npsql (9.1.4)\n3\nType \"help\" for help.\n4\nmarkw=# create database news;\n5\nCREATE DATABASE\nWe will use three example programs that you can find in the src/clsql_examples directory in the\nbook repository on github:\n• clsql_create_news_schema.lisp to create table “articles” in database “news”\n• clsql_write_to_news.lisp to write test data to table “articles”\n• clsql_read_from_news.lisp to read from the table “articles”\nThe following listing shows the file src/clsql_examples/clsql_create_news_schema.lisp:\n1\n(ql:quickload :clsql)\n2\n(ql:quickload :clsql-postgresql)\n3\n4\n;; Postgres connection specification:\n5\n;;\n(host db user password &optional port options tty).\n6\n;; The first argument to **clsql:connect** is a connection\n7\n;; specification list:\n8\n9\n(clsql:connect '(\"localhost\" \"news\" \"markw\" nil)\n10\n:database-type :postgresql)\n11\n12\n(clsql:def-view-class articles ()\n13\n((id\n14\n:db-kind :key\n15\n:db-constraints :not-null\n16\n:type integer\n17\n:initarg :id)\n18\n(uri\n19\n:accessor uri\n20\n:type (string 60)\n21\n:initarg :uri)\n22\n(title\n23\n:accessor title\n24\n:type (string 90)\n25\n:initarg :title)\n26\n(text\n27\n:accessor text\n\nAccessing Relational Databases\n93\n28\n:type (string 500)\n29\n:nulls-ok t\n30\n:initarg :text)))\n31\n32\n(defun create-articles-table ()\n33\n(clsql:create-view-from-class 'articles))\nIn this repl listing, we create the database table “articles” using the function create-articles-table\nthat we just defined:\n1\n->\nsrc git:(master) sbcl\n2\n(running SBCL from: /Users/markw/sbcl)\n3\n* (load \"clsql_create_news_schema.lisp\")\n4\n* (create-articles-table)\n5\nNOTICE:\nCREATE TABLE / PRIMARY KEY will create implicit index\n6\n\"article_pk\" for table \"articles\"\n7\nT\n8\n*\nThe following listing shows the file src/clsql_examples/clsql_write_to_news.lisp:\n1\n(ql:quickload :clsql)\n2\n(ql:quickload :clsql-postgresql)\n3\n4\n;; Open connection to database and create CLOS class and database view\n5\n;; for table 'articles':\n6\n(load \"clsql_create_news_schema.lisp\")\n7\n8\n(defvar *a1*\n9\n(make-instance\n10\n'article\n11\n:uri \"http://test.com\"\n12\n:title \"Trout Season is Open on Oak Creek\"\n13\n:text \"State Fish and Game announced the opening of trout season\"))\n14\n15\n(clsql:update-records-from-instance *a1*)\n16\n;; modify a slot value and update database:\n17\n(setf (slot-value *a1* 'title) \"Trout season is open on Oak Creek!!!\")\n18\n(clsql:update-records-from-instance *a1*)\n19\n;; warning: the last statement changes the \"id\" column in the table\nYou should load the file clsql_write_to_news.lisp one time in a repl to create the test data. The\nfollowing listing shows file clsql_read_from_news.lisp:\n\nAccessing Relational Databases\n94\n1\n(ql:quickload :clsql)\n2\n(ql:quickload :clsql-postgresql)\n3\n4\n;; Open connection to database and create CLOS class and database view\n5\n;; for table 'articles':\n6\n(load \"clsql_create_news_schema.lisp\")\n7\n8\n(defun pp-article (article)\n9\n(format t\n10\n\"~%URI: ~S ~%Title: ~S ~%Text: ~S ~%\"\n11\n(slot-value article 'uri)\n12\n(slot-value article 'title)\n13\n(slot-value article 'text)))\n14\n15\n(dolist (a (clsql:select 'article))\n16\n(pp-article (car a)))\nLoading the file clsql_read_from_news.lisp produces the following output:\n1\nURI: \"http://test.com\"\n2\nTitle: \"Trout season is open on Oak Creek!!!\"\n3\nText: \"State Fish and Game announced the opening of trout season\"\n4\n5\nURI: \"http://example.com\"\n6\nTitle: \"Longest day of year\"\n7\nText: \"The summer solstice is on Friday.\"\nYou can also embed SQL where clauses in queries:\n(dolist (a (clsql:select 'article :where \"title like '%season%'\"))\n(pp-article (car a)))\nwhich produces this output:\n1\nURI: \"http://test.com\"\n2\nTitle: \"Trout season is open on Oak Creek!!!\"\n3\nText: \"State Fish and Game announced the opening of\n4\ntrout season\"\nIn this example, I am using a SQL like expression to perform partial text matching.\n\nAccessing Relational Databases\n95\nDatabase Wrap Up\nYou learned the basics for accessing relational databases. When I am designing new systems for\nprocessing data I like to think of my Common Lisp code as being purely functional: my Lisp functions\naccept arguments that they do not modify and return results. I like to avoid side effects, that is\nchanging global state. When I do have to handle mutable state (or data) I prefer storing mutable state\nin an external database. I use this same approach when I use the Haskell functional programming\nlanguage.\n\nUsing MongoDB, Solr NoSQL Data\nStores\nNon-relational data stores are commonly used for applications that don’t need either full relational\nalgebra or must scale.\nThe MongoDB example code is in the file src/loving_snippets/mongo_news.lisp. The Solr example\ncode is in the subdirectories src/solr_examples.\nNote for the fifth edition: The Common Lisp cl-mongo library is now unsupported for versions of\nMongoDB later than 2.6 (released in 2016). You can install an old version of MongoDB for macOS⁴²\nor for Linux⁴³. I have left the MongoDB examples in this section but I can’t recommend that you use\ncl-mongo and MongoDB for any serious applications.\nBrewer’s CAP theorem states that a distributed data storage system comprised of multiple nodes can\nbe robust to two of three of the following guarantees: all nodes always have a Consistent view of the\nstate of data, general Availablity of data if not all nodes are functioning, and Partition tolerance so\nclients can still communicate with the data storage system when parts of the system are unavailable\nbecause of network failures. The basic idea is that different applications have different requirements\nand sometimes it makes sense to reduce system cost or improve scalability by easing back on one of\nthese requirements.\nA good example is that some applications may not need transactions (the first guarantee) because it\nis not important if clients sometimes get data that is a few seconds out of date.\nMongoDB allows you to choose consistency vs. availability vs. efficiency.\nI cover the Solr indexing and search service (based on Lucene) both because a Solr indexed document\nstore is a type of NoSQL data store and also because I believe that you will find Solr very useful for\nbuilding systems, if you don’t already use it.\nMongoDB\nThe following discussion of MongoDB is based on just my personal experience, so I am not covering\nall use cases. I have used MongoDB for:\n• Small clusters of MongoDB nodes to analyze social media data, mostly text mining and\nsentiment analysis. In all cases for each application I ran MongoDB with one write master\n⁴²https://www.mongodb.org/dl/osx\n⁴³https://www.mongodb.org/dl/linux\n\nUsing MongoDB, Solr NoSQL Data Stores\n97\n(i.e., I wrote data to this one node but did not use it for reads) and multiple read-only slave\nnodes. Each slave node would run on the same server that was usually performing a single bit\nof analytics.\n• Multiple very large independent clusters for web advertising. Problems faced included trying\nto have some level of consistency across data centers. Replica sets were used within each data\ncenter.\n• Running a single node MongoDB instance for low volume data collection and analytics.\nOne of the advantages of MongoDB is that it is very “developer friendly” because it supports ad-\nhoc document schemas and interactive queries. I mentioned that MongoDB allows you to choose\nconsistency vs. availability vs. efficiency. When you perform MongoDB writes you can specify some\ngranularity of what constitutes a “successful write” by requiring that a write is performed at a\nspecific number of nodes before the client gets acknowledgement that the write was successful. This\nrequirement adds overhead to each write operation and can cause writes to fail if some nodes are\nnot available.\nThe MongoDB online documentation⁴⁴ is very good. You don’t have to read it in order to have fun\nplaying with the following Common Lisp and MongoDB examples, but if you find that MongoDB is\na good fit for your needs after playing with these examples then you should read the documentation.\nI usually install MongoDB myself but it is sometimes convenient to use a hosting service. There are\nseveral well regarded services and I have used MongoHQ⁴⁵.\nAt this time there is no official Common Lisp support for accessing MongoDB but there is a useful\nproject by Alfons Haffmans’ cl-mongo⁴⁶ that will allow us to write Common Lisp client applications\nand have access to most of the capabilities of MongoDB.\nThe file src/mongo_news.lisp contains the example code used in the next three sessions.\nAdding Documents\nThe following repl listing shows the cl-mongo APIs for creating a new document, adding elements\n(attributes) to it, and inserting it in a MongoDB data store:\n⁴⁴http://docs.mongodb.org/manual/\n⁴⁵https://www.mongohq.com/\n⁴⁶https://github.com/fons/cl-mongo\n\nUsing MongoDB, Solr NoSQL Data Stores\n98\n(ql:quickload \"cl-mongo\")\n(cl-mongo:db.use \"news\")\n(defun add-article (uri title text)\n(let ((doc (cl-mongo:make-document)))\n(cl-mongo:add-element \"uri\" uri doc)\n(cl-mongo:add-element \"title\" title doc)\n(cl-mongo:add-element \"text\" text doc)\n(cl-mongo:db.insert \"article\" doc)))\n;; add a test document:\n(add-article \"http://test.com\" \"article title 1\" \"article text 1\")\nIn this example, three string attributes were added to a new document before it was saved.\nFetching Documents by Attribute\nWe will start by fetchng and pretty-printing all documents in the collection articles and fetching all\narticles a list of nested lists where the inner nested lists are document URI, title, and text:\n1\n(defun print-articles ()\n2\n(cl-mongo:pp (cl-mongo:iter (cl-mongo:db.find \"article\" :all))))\n3\n4\n;; for each document, use the cl-mongo:get-element on\n5\n;; each element we want to save:\n6\n(defun article-results->lisp-data (mdata)\n7\n(let ((ret '()))\n8\n;;(print (list \"size of result=\" (length mdata)))\n9\n(dolist (a mdata)\n10\n;;(print a)\n11\n(push\n12\n(list\n13\n(cl-mongo:get-element \"uri\" a)\n14\n(cl-mongo:get-element \"title\" a)\n15\n(cl-mongo:get-element \"text\" a))\n16\nret)))\n17\nret))\n18\n19\n(defun get-articles ()\n20\n(article-results->lisp-data\n21\n(cadr (cl-mongo:db.find \"article\" :all))))\nOutput for these two functions looks like:\n\nUsing MongoDB, Solr NoSQL Data Stores\n99\n1\n* (print-articles)\n2\n3\n{\n4\n\"_id\" -> objectid(99778A792EBB4F76B82F75C6)\n5\n\"uri\"\n->\nhttp://test.com/3\n6\n\"title\"\n->\narticle title 3\n7\n\"text\"\n->\narticle text 3\n8\n}\n9\n10\n{\n11\n\"_id\" -> objectid(D47DEF3CFDB44DEA92FD9E56)\n12\n\"uri\"\n->\nhttp://test.com/2\n13\n\"title\"\n->\narticle title 2\n14\n\"text\"\n->\narticle text 2\n15\n}\n16\n17\n* (get-articles)\n18\n19\n((\"http://test.com/2\" \"article title 2\" \"article text 2\")\n20\n(\"http://test.com/3\" \"article title 3\" \"article text 3\"))\nFetching Documents by Regular Expression Text Search\nBy reusing the function article-results->lisp-data defined in the last section, we can also search for\nJSON documents using regular expressions matching attribute values:\n1\n;; find documents where substring 'str' is in the title:\n2\n(defun search-articles-title (str)\n3\n(article-results->lisp-data\n4\n(cadr\n5\n(cl-mongo:iter\n6\n(cl-mongo:db.find\n7\n\"article\"\n8\n(cl-mongo:kv\n9\n\"title\"\n// TITLE ATTRIBUTE\n10\n(cl-mongo:kv \"$regex\" str)) :limit 10)))))\n11\n12\n;; find documents where substring 'str' is in the text element:\n13\n(defun search-articles-text (str)\n14\n(article-results->lisp-data\n15\n(cadr\n16\n(cl-mongo:db.find\n\nUsing MongoDB, Solr NoSQL Data Stores\n100\n17\n\"article\"\n18\n(cl-mongo:kv\n19\n\"text\"\n// TEXT ATTRIBUTE\n20\n(cl-mongo:kv \"$regex\" str)) :limit 10))))\nI set the limit to return a maximum of ten documents. If you do not set the limit, this example code\nonly returns one search result. The following repl listing shows the results from calling function\nsearch-articles-text:\n1\n* (SEARCH-ARTICLES-TEXT \"text\")\n2\n3\n((\"http://test.com/2\" \"article title 2\" \"article text 2\")\n4\n(\"http://test.com/3\" \"article title 3\" \"article text 3\"))\n5\n* (SEARCH-ARTICLES-TEXT \"3\")\n6\n7\n((\"http://test.com/3\" \"article title 3\" \"article text 3\"))\nI find using MongoDB to be especially effective when experimenting with data and code. The schema\nfree JSON document format, using interactive queries using the mongo shell⁴⁷, and easy to use client\nlibraries like clouchdb for Common Lisp will let you experiment with a lot of ideas in a short period\nof time. The following listing shows the use of the interactive mongo shell. The database news is\nthe database used in the MongoDB examples in this chapter; you will notice that I also have other\ndatabases for other projects on my laptop:\n1\n->\nsrc git:(master) mongo\n2\nMongoDB shell version: 2.4.5\n3\nconnecting to: test\n4\n> show dbs\n5\nkbsportal\n0.03125GB\n6\nknowledgespace\n0.03125GB\n7\nlocal\n(empty)\n8\nmark_twitter\n0.0625GB\n9\nmyfocus\n0.03125GB\n10\nnews\n0.03125GB\n11\nnyt\n0.125GB\n12\ntwitter\n0.125GB\n13\n> use news\n14\nswitched to db news\n15\n> show collections\n16\narticle\n17\nsystem.indexes\n⁴⁷http://docs.mongodb.org/manual/mongo/\n\nUsing MongoDB, Solr NoSQL Data Stores\n101\n18\n> db.article.find()\n19\n{ \"uri\" : \"http://test.com/3\",\n20\n\"title\" : \"article title 3\",\n21\n\"text\" : \"article text 3\",\n22\n\"_id\" : ObjectId(\"99778a792ebb4f76b82f75c6\") }\n23\n{ \"uri\" : \"http://test.com/2\",\n24\n\"title\" : \"article title 2\",\n25\n\"text\" : \"article text 2\",\n26\n\"_id\" : ObjectId(\"d47def3cfdb44dea92fd9e56\") }\n27\n>\nLine 1 of this listing shows starting the mongo shell. Line 4 shows how to list all databases in the\ndata store. In line 13 I select the database “news” to use. Line 15 prints out the names of all collections\nin the current database “news”. Line 18 prints out all documents in the “articles” collection. You can\nread the documentation for the mongo shell⁴⁸ for more options like selective queries, adding indices,\netc.\nWhen you run a MongoDB service on your laptop, also try the admin interface on http://localhost:28017/⁴⁹.\nA Common Lisp Solr Client\nThe Lucene project is one of the most widely used Apache Foundation projects. Lucene is a flexible\nlibrary for preprocessing and indexing text, and searching text. I have personally used Lucene on\nso many projects that it would be difficult to count them. The Apache Solr Project⁵⁰ adds a network\ninterface to the Lucene text indexer and search engine. Solr also adds other utility features to Lucene:\n• While Lucene is a library to embed in your programs, Solr is a complete system.\n• Solr provides good defaults for preprocessing and indexing text and also provides rich support\nfor managing structured data.\n• Provides both XML and JSON APIs using HTTP and REST.\n• Supports faceted search, geospatial search, and provides utilities for highlighting search terms\nin surrounding text of search results.\n• If your system ever grows to a very large number of users, Solr supports scaling via replication.\nI hope that you will find the Common Lisp example Solr client code in the following sections helps\nyou make Solr part of large systems that you write using Common Lisp.\nInstalling Solr\nDownload a binary Solr distribution⁵¹ and un-tar or un-zip this Solr distribution, cd to the\ndistribution directory, then cd to the example directory and run:\n⁴⁸http://docs.mongodb.org/manual/mongo/\n⁴⁹http://localhost:28017/\n⁵⁰https://lucene.apache.org/solr/\n⁵¹https://lucene.apache.org/solr/downloads.html\n\nUsing MongoDB, Solr NoSQL Data Stores\n102\n1\n~/solr/example>\njava -jar start.jar\nYou can access the Solr Admin Web App at http://localhost:8983/solr/#/⁵². This web app can be seen\nin the following screen shot:\nSolr Admin Web App\nThere is no data in the Solr example index yet, so following the Solr tutorial instructions:\n⁵²http://localhost:8983/solr/#/\n\nUsing MongoDB, Solr NoSQL Data Stores\n103\n1\n~/> cd ~/solr/example/exampledocs\n2\n~/solr/example/exampledocs> java -jar post.jar *.xml\n3\nSimplePostTool version 1.5\n4\nPosting files to base url http://localhost:8983/solr/update\n5\nusing content-type application/xml..\n6\nPOSTing file gb18030-example.xml\n7\nPOSTing file hd.xml\n8\nPOSTing file ipod_other.xml\n9\nPOSTing file ipod_video.xml\n10\nPOSTing file manufacturers.xml\n11\nPOSTing file mem.xml\n12\nPOSTing file money.xml\n13\nPOSTing file monitor.xml\n14\nPOSTing file monitor2.xml\n15\nPOSTing file mp500.xml\n16\nPOSTing file sd500.xml\n17\nPOSTing file solr.xml\n18\nPOSTing file utf8-example.xml\n19\nPOSTing file vidcard.xml\n20\n14 files indexed.\n21\nCOMMITting Solr index changes\n22\nto http://localhost:8983/solr/update..\n23\nTime spent: 0:00:00.480\nYou will learn how to add documents to Solr directly in your Common Lisp programs in a later\nsection.\nAssuming that you have a fast Internet connection so that downloading Solr was quick, you have\nhopefully spent less than five or six minutes getting Solr installed and running with enough example\nsearch data for the Common Lisp client examples we will play with. Solr is a great tool for storing,\nindexing, and searching data. I recommend that you put off reading the official Solr documentation\nfor now and instead work through the Common Lisp examples in the next two sections. Later, if\nyou want to use Solr then you will need to carefully read the Solr documentation.\nSolr’s REST Interface\nThe Solr REST Interface Documentation⁵³ documents how to perform search using HTTP GET\nrequests. All we need to do is implement this in Common Lisp which you will see is easy.\nAssuming that you have Solr running and the example data loaded, we can try searching for docu-\nments with, for example, the word “British” using the URL http://localhost:8983/solr/select?q=British⁵⁴.\nThis is a REST request URL and you can use utilities like curl or wget to fetch the XML data. I fetched\n⁵³https://wiki.apache.org/solr/SolJSON\n⁵⁴http://localhost:8983/solr/select?q=British\n\nUsing MongoDB, Solr NoSQL Data Stores\n104\nthe data in a web browser, as seen in the following screen shot of a Firefox web browser (I like the\nway Firefox formats and displays XML data):\nSolr Search Results as XML Data\nThe attributes in the returned search results need some explanation. We indexed several example\nXML data files, one of which contained the following XML element that we just saw as a search\nresult:\n1\n<doc>\n2\n<field name=\"id\">GBP</field>\n3\n<field name=\"name\">One British Pound</field>\n4\n<field name=\"manu\">U.K.</field>\n5\n<field name=\"manu_id_s\">uk</field>\n6\n<field name=\"cat\">currency</field>\n7\n<field name=\"features\">Coins and notes</field>\n8\n<field name=\"price_c\">1,GBP</field>\n9\n<field name=\"inStock\">true</field>\n10\n</doc>\n\nUsing MongoDB, Solr NoSQL Data Stores\n105\nSo, the search result has the same attributes as the structured XML data that was added to the Solr\nsearch index. Solr’s capability for indexing structured data is a superset of just indexing plain text.\nIf for example we were indexing news stories, then example input data might look like:\n1\n<doc>\n2\n<field name=\"id\">new_story_0001</field>\n3\n<field name=\"title\">Fishing Season Opens</field>\n4\n<field name=\"text\">Fishing season opens on Friday in Oak Creek.</field>\n5\n</doc>\nWith this example, a search result that returned this document as a result would return attributes\nid, title, and text, and the values of these three attributes.\nBy default the Solr web service returns XML data as seen in the last screen shot. For our examples, I\nprefer using JSON so we are going to always add a request parameter wt=json to all REST calls. The\nfollowing screen shot shows the same data returned in JSON serialization format instead of XML\nformat of a Chrome web browser (I like the way Chrome formats and displays JSON data with the\nJSONView Chrome Browser extension):\nSolr Search Results as JSON Data\n\nUsing MongoDB, Solr NoSQL Data Stores\n106\nYou can read the full JSON REST Solr documentation later, but for our use here we will use the\nfollowing search patterns:\n• http://localhost:8983/solr/select?q=British+One&wt=json - search for documents with either of\nthe words “British” or “one” in them. Note that in URIs that the “+” character is used to encode\na space character. If you wanted a “+” character you would encode it with “%2B” and a space\ncharacter is encoded as “%20”. The default Solr search option is an OR of the search terms,\nunlike, for example, Google Search.\n• http://localhost:8983/solr/select?q=British+AND+one&wt=json - search for documents that\ncontain both of the words “British” and “one” in them. The search term in plain text is “British\nAND one”.\nCommon Lisp Solr Client for Search\nAs we sawearlier in Network Programming it is fairly simple to use the drakma and cl-json\nCommon Lisp libraries to call REST services that return JSON data. The function do-search defined\nin the next listing (all the Solr example code is in the file src/solr-client.lisp) constructs a query\nURI as we saw in the last section and uses the Drackma library to perform an HTTP GET operation\nand the cl-json library to parse the returned string containing JSON data into Lisp data structures:\n(ql:quickload :drakma)\n(ql:quickload :cl-json)\n(defun do-search (&rest terms)\n(let ((query-string (format nil \"~{~A~^+AND+~}\" terms)))\n(cl-json:decode-json-from-string\n(drakma:http-request\n(concatenate\n'string\n\"http://localhost:8983/solr/select?q=\"\nquery-string\n\"&wt=json\")))))\nThis example code does return the search results as Lisp list data; for example:\n\nUsing MongoDB, Solr NoSQL Data Stores\n107\n1\n* (do-search \"British\" \"one\")\n2\n3\n((:RESPONSE-HEADER (:STATUS . 0) (:*Q-TIME . 1)\n4\n(:PARAMS (:Q . \"British+AND+one\") (:WT . \"json\")))\n5\n(:RESPONSE (:NUM-FOUND . 6) (:START . 0)\n6\n(:DOCS\n7\n((:ID . \"GBP\") (:NAME . \"One British Pound\") (:MANU . \"U.K.\")\n8\n(:MANU--ID--S . \"uk\") (:CAT \"currency\")\n9\n(:FEATURES \"Coins and notes\")\n10\n(:PRICE--C . \"1,GBP\") (:IN-STOCK . T)\n11\n(:--VERSION-- . 1440194917628379136))\n12\n((:ID . \"USD\") (:NAME . \"One Dollar\")\n13\n(:MANU . \"Bank of America\")\n14\n(:MANU--ID--S . \"boa\") (:CAT \"currency\")\n15\n(:FEATURES \"Coins and notes\")\n16\n(:PRICE--C . \"1,USD\") (:IN-STOCK . T)\n17\n(:--VERSION-- . 1440194917624184832))\n18\n((:ID . \"EUR\") (:NAME . \"One Euro\")\n19\n(:MANU . \"European Union\")\n20\n(:MANU--ID--S . \"eu\") (:CAT \"currency\")\n21\n(:FEATURES \"Coins and notes\")\n22\n(:PRICE--C . \"1,EUR\") (:IN-STOCK . T)\n23\n(:--VERSION-- . 1440194917626281984))\n24\n((:ID . \"NOK\") (:NAME . \"One Krone\")\n25\n(:MANU . \"Bank of Norway\")\n26\n(:MANU--ID--S . \"nor\") (:CAT \"currency\")\n27\n(:FEATURES \"Coins and notes\")\n28\n(:PRICE--C . \"1,NOK\") (:IN-STOCK . T)\n29\n(:--VERSION-- . 1440194917631524864))\n30\n((:ID . \"0579B002\")\n31\n(:NAME . \"Canon PIXMA MP500 All-In-One Photo Printer\")\n32\n(:MANU . \"Canon Inc.\")\n33\n(:MANU--ID--S . \"canon\")\n34\n(:CAT \"electronics\" \"multifunction printer\"\n35\n\"printer\" \"scanner\" \"copier\")\n36\n(:FEATURES \"Multifunction ink-jet color photo printer\"\n37\n\"Flatbed scanner, optical scan resolution of 1,200 x 2,400 dpi\"\n38\n\"2.5\\\" color LCD preview screen\" \"Duplex Copying\"\n39\n\"Printing speed up to 29ppm black, 19ppm color\" \"Hi-Speed USB\"\n40\n\"memory card: CompactFlash, Micro Drive, SmartMedia,\n41\nMemory Stick, Memory Stick Pro, SD Card, and MultiMediaCard\")\n42\n(:WEIGHT . 352.0) (:PRICE . 179.99)\n43\n(:PRICE--C . \"179.99,USD\")\n\nUsing MongoDB, Solr NoSQL Data Stores\n108\n44\n(:POPULARITY . 6) (:IN-STOCK . T)\n45\n(:STORE . \"45.19214,-93.89941\")\n46\n(:--VERSION-- . 1440194917651447808))\n47\n((:ID . \"SOLR1000\")\n48\n(:NAME . \"Solr, the Enterprise Search Server\")\n49\n(:MANU . \"Apache Software Foundation\")\n50\n(:CAT \"software\" \"search\")\n51\n(:FEATURES \"Advanced Full-Text Search Capabilities using Lucene\"\n52\n\"Optimized for High Volume Web Traffic\"\n53\n\"Standards Based Open Interfaces - XML and HTTP\"\n54\n\"Comprehensive HTML Administration Interfaces\"\n55\n\"Scalability - Efficient Replication to other Solr Search Servers\"\n56\n\"Flexible and Adaptable with XML configuration and Schema\"\n57\n\"Good unicode support: hÃ©llo (hello with an accent over the e)\")\n58\n(:PRICE . 0.0) (:PRICE--C . \"0,USD\") (:POPULARITY . 10) (:IN-STOCK . T)\n59\n(:INCUBATIONDATE--DT . \"2006-01-17T00:00:00Z\")\n60\n(:--VERSION-- . 1440194917671370752)))))\nI might modify the search function to return just the fetched documents as a list, discarding the\nreturned Solr meta data:\n1\n* (cdr (cadddr (cadr (do-search \"British\" \"one\"))))\n2\n3\n(((:ID . \"GBP\") (:NAME . \"One British Pound\") (:MANU . \"U.K.\")\n4\n(:MANU--ID--S . \"uk\") (:CAT \"currency\") (:FEATURES \"Coins and notes\")\n5\n(:PRICE--C . \"1,GBP\") (:IN-STOCK . T)\n6\n(:--VERSION-- . 1440194917628379136))\n7\n((:ID . \"USD\") (:NAME . \"One Dollar\") (:MANU . \"Bank of America\")\n8\n(:MANU--ID--S . \"boa\") (:CAT \"currency\") (:FEATURES \"Coins and notes\")\n9\n(:PRICE--C . \"1,USD\") (:IN-STOCK . T)\n10\n(:--VERSION-- . 1440194917624184832))\n11\n((:ID . \"EUR\") (:NAME . \"One Euro\") (:MANU . \"European Union\")\n12\n(:MANU--ID--S . \"eu\") (:CAT \"currency\") (:FEATURES \"Coins and notes\")\n13\n(:PRICE--C . \"1,EUR\") (:IN-STOCK . T)\n14\n(:--VERSION-- . 1440194917626281984))\n15\n((:ID . \"NOK\") (:NAME . \"One Krone\") (:MANU . \"Bank of Norway\")\n16\n(:MANU--ID--S . \"nor\") (:CAT \"currency\")\n17\n(:FEATURES \"Coins and notes\")\n18\n(:PRICE--C . \"1,NOK\") (:IN-STOCK . T)\n19\n(:--VERSION-- . 1440194917631524864))\n20\n((:ID . \"0579B002\")\n21\n(:NAME . \"Canon PIXMA MP500 All-In-One Photo Printer\")\n22\n(:MANU . \"Canon Inc.\") (:MANU--ID--S . \"canon\")\n\nUsing MongoDB, Solr NoSQL Data Stores\n109\n23\n(:CAT \"electronics\" \"multifunction printer\" \"printer\"\n24\n\"scanner\" \"copier\")\n25\n(:FEATURES \"Multifunction ink-jet color photo printer\"\n26\n\"Flatbed scanner, optical scan resolution of 1,200 x 2,400 dpi\"\n27\n\"2.5\\\" color LCD preview screen\" \"Duplex Copying\"\n28\n\"Printing speed up to 29ppm black, 19ppm color\" \"Hi-Speed USB\"\n29\n\"memory card: CompactFlash, Micro Drive, SmartMedia, Memory Stick,\n30\nMemory Stick Pro, SD Card, and MultiMediaCard\")\n31\n(:WEIGHT . 352.0) (:PRICE . 179.99) (:PRICE--C . \"179.99,USD\")\n32\n(:POPULARITY . 6) (:IN-STOCK . T) (:STORE . \"45.19214,-93.89941\")\n33\n(:--VERSION-- . 1440194917651447808))\n34\n((:ID . \"SOLR1000\") (:NAME . \"Solr, the Enterprise Search Server\")\n35\n(:MANU . \"Apache Software Foundation\") (:CAT \"software\" \"search\")\n36\n(:FEATURES \"Advanced Full-Text Search Capabilities using Lucene\"\n37\n\"Optimized for High Volume Web Traffic\"\n38\n\"Standards Based Open Interfaces - XML and HTTP\"\n39\n\"Comprehensive HTML Administration Interfaces\"\n40\n\"Scalability - Efficient Replication to other Solr Search Servers\"\n41\n\"Flexible and Adaptable with XML configuration and Schema\"\n42\n\"Good unicode support: hÃ©llo (hello with an accent over the e)\")\n43\n(:PRICE . 0.0) (:PRICE--C . \"0,USD\") (:POPULARITY . 10) (:IN-STOCK . T)\n44\n(:INCUBATIONDATE--DT . \"2006-01-17T00:00:00Z\")\n45\n(:--VERSION-- . 1440194917671370752)))\nThere are a few more important details if you want to add Solr search to your Common Lisp\napplications. When there are many search results you might want to fetch a limited number of\nresults and then “page” through them. The following strings can be added to the end of a search\nquery:\n• &rows=2 this example returns a maximum of two “rows” or two query results.\n• &start=4 this example skips the first 4 available results\nA query that combines skipping results and limiting the number of returned results looks like this:\n1\nhttp://localhost:8983/solr/select?q=British+One&wt=json&start=2&rows=2\nCommon Lisp Solr Client for Adding Documents\nIn the last example we relied on adding example documents to the Solr search index using the\ndirections for setting up a new Solr installation. In a real application, in addition to performing search\nrequests for indexed documents you will need to add new documents from your Lisp applications.\nUsing the Drakma we will see that it is very easy to add documents.\nWe need to construct a bit of XML containing new documents in the form:\n\nUsing MongoDB, Solr NoSQL Data Stores\n110\n1\n<add>\n2\n<doc>\n3\n<field name=\"id\">123456</field>\n4\n<field name=\"title\">Fishing Season</field>\n5\n</doc>\n6\n</add>\nYou can specify whatever field names (attributes) that are required for your application. You can\nalso pass multiple <doc></doc> elements in one add request. We will want to specify documents in\na Lisp-like way: a list of cons values where each cons value is a field name and a value. For the last\nXML document example we would like an API that lets us just deal with Lisp data like:\n(do-add '((\"id\" . \"12345\")\n(\"title\" . \"Fishing Season\")))\nOne thing to note: the attribute names and values must be passed as strings. Other data types like\nintegers, floating point numbers, structs, etc. will not work.\nThis is nicer than having to use XML, right? The first thing we need is a function to convert a list\nof cons values to XML. I could have used the XML Builder functionality in the cxml library that is\navailable via Quicklisp, but for something this simple I just wrote it in pure Common Lisp with no\nother dependencies (also in the example file src/solr-client.lisp) :\n1\n(defun keys-values-to-xml-string\n(keys-values-list)\n2\n(with-output-to-string (stream)\n3\n(format stream \"<add><doc>\")\n4\n(dolist (kv keys-values-list)\n5\n(format stream \"<field name=\\\"\")\n6\n(format stream (car kv))\n7\n(format stream \"\\\">\")\n8\n(format stream (cdr kv))\n9\n(format stream \"\\\"</field>\"))\n10\n(format stream \"</doc></add>\")))\nThe macro with-output-to-string on line 2 of the listing is my favorite way to generate strings.\nEverything written to the variable stream inside the macro call is appended to a string; this string\nis the return value of the macro.\nThe following function adds documents to the Solr document input queue but does not actually\nindex them:\n\nUsing MongoDB, Solr NoSQL Data Stores\n111\n1\n(defun do-add (keys-values-list)\n2\n(drakma:http-request\n3\n\"http://localhost:8983/solr/update\"\n4\n:method :post\n5\n:content-type \"application/xml\"\n6\n:content ( keys-values-to-xml-string\nkeys-values-list)))\nYou have noticed in line 3 that I am accessing a Solr server running on localhost and not a remote\nserver. In an application using a remote Solr server you would need to modify this to reference your\nserver; for example:\n1\n\"http://solr.knowledgebooks.com:8983/solr/update\"\nFor efficiency Solr does not immediately add new documents to the index until you commit the\nadditions. The following function should be called after you are done adding documents to actually\nadd them to the index:\n(defun commit-adds ()\n(drakma:http-request\n\"http://localhost:8983/solr/update\"\n:method :post\n:content-type \"application/xml\"\n:content \"<commit></commit>\"))\nNotice that all we need is an empty element <commit></commit> that signals the Solr server that\nit should index all recently added documents. The following repl listing shows everything working\ntogether (I am assuming that the contents of the file src/solr-client.lisp has been loaded); not all of\nthe output is shown in this listing:\n* (do-add '((\"id\" . \"12345\") (\"title\" . \"Fishing Season\")))\n200\n((:CONTENT-TYPE . \"application/xml; charset=UTF-8\")\n(:CONNECTION . \"close\"))\n#<PURI:URI http://localhost:8983/solr/update>\n#<FLEXI-STREAMS:FLEXI-IO-STREAM {1009193133}>\nT\n\"OK\"\n* (commit-adds)\n200\n((:CONTENT-TYPE . \"application/xml; charset=UTF-8\")\n\nUsing MongoDB, Solr NoSQL Data Stores\n112\n(:CONNECTION . \"close\"))\n#<PURI:URI http://localhost:8983/solr/update>\n#<FLEXI-STREAMS:FLEXI-IO-STREAM {10031F20B3}>\nT\n\"OK\"\n* (do-search \"fishing\")\n((:RESPONSE-HEADER (:STATUS . 0) (:*Q-TIME . 2)\n(:PARAMS (:Q . \"fishing\") (:WT . \"json\")))\n(:RESPONSE (:NUM-FOUND . 1) (:START . 0)\n(:DOCS\n((:ID . \"12345\\\"\") (:TITLE \"Fishing Season\\\"\")\n(:--VERSION-- . 1440293991717273600)))))\n*\nCommon Lisp Solr Client Wrap Up\nSolr has a lot of useful features that we have not used here like supporting faceted search (drilling\ndown in previous search results), geolocation search, and looking up indexed documents by attribute.\nIn the examples I have shown you, all text fields are indexed but Solr optionally allows you fine\ncontrol over indexing, spelling correction, word stemming, etc.\nSolr is a very capable tool for storing, indexing, and searching data. I have seen Solr used effectively\non projects as a replacement for a relational database or other NoSQL data stores like CouchDB or\nMongoDB. There is a higher overhead for modifying or removing data in Solr so for applications\nthat involve frequent modifications to stored data Solr might not be a good choice.\nNoSQL Wrapup\nThere are more convenient languages than Common Lisp to use for accessing MongoDB. To be\nhonest, my favorites are Ruby and Clojure. That said, for applications where the advantages of\nCommon Lisp are compelling, it is good to know that your Common Lisp applications can play\nnicely with MongoDB.\nI am a polyglot programmer: I like to use the best programming language for any specific job. When\nwe design and build systems with more than one programming language, there are several options\nto share data:\n• Use foreign function interfaces to call one language from another from inside one process.\n• Use a service architecture and send requests using REST or SOAP.\n• Use shared data stores, like relational databases, MongoDB, CouchDB and Solr.\nHopefully this chapter and the last chapter will provide most of what you need for the last option.\n\nNatural Language Processing\nNatural Language Processing (NLP) is the automated processing of natural language text with\nseveral goals:\n• Determine the parts of speech (POS tagging) of words based on the surrounding words.\n• Detect if two text documents are similar.\n• Categorize text (e.g., is it about the economy, politics, sports, etc.)\n• Summarize text\n• Determine the sentiment of text\n• Detect names (e.g., place names, people’s names, product names, etc.)\nWe will use a library that I wrote that performs POS tagging, categorization (classification),\nsummarization, and detects proper names.\nMy example code for this chapter is contained in separate Quicklisp projects located in the\nsubdirectories:\n• src/fasttag: performs part of speech tagging and tokenizes text\n• src/categorize_summarize: performs categorization (e.g., detects the topic of text is news,\npolitics, economy, etc.) and text summarization\n• src/kbnlp: the top level APIs for my pure Common Lisp natural language processing (NLP)\ncode. In later chapters we will take a different approach by using Python deep learning models\nfor NLP that we call as a web service. I use both approaches in my own work.\nI worked on this Lisp code, and also similar code in Java, from about 2001 to 2011, and again in 2019\nfor my application for generating knowledge graph data automatically (this is an example in a later\nchapter). I am going to begin the next section with a quick explanation of how to run the example\ncode. If you find the examples interesting then you can also read the rest of this chapter where I\nexplain how the code works.\nThe approach that I used in my library for categorization (word counts) is now dated. I recommend\nthat you consider taking Andrew Ng’s course on Machine Learning on the free online Coursera\nsystem and then take one of the Coursera NLP classes for a more modern treatment of NLP.\nIn addition to the code for my library you might also find the linguistic data in src/linguistic_data\nuseful.\nLoading and Running the NLP Library\nI repackaged the NLP example code into one long file. The code used to be split over 18 source files.\nThe code should be loaded from the src/kbnlp directory:\n\nNatural Language Processing\n114\n1\n%\nloving-common-lisp git:(master) > cd src/kbnlp\n2\n%\nsrc/kbnlp git:(master) > sbcl\n3\n* (ql:quickload \"kbnlp\")\n4\n5\n\"Startng to load data....\"\n6\n\"....done loading data.\"\n7\n*\nThis also loads the projects in src/fasttag and src/categorize_summarize.\nUnfortunately, it takes about a minute using SBCL to load the required linguistic data so I\nrecommend creating a Lisp image that can be reloaded to avoid the time required to load the data:\n1\n* (sb-ext:save-lisp-and-die \"nlp-image\" :purify t)\n2\n[undoing binding stack and other enclosing state... done]\n3\n[saving current Lisp image into nlp-image:\n4\nwriting 5280 bytes from the read-only space at 0x0x20000000\n5\nwriting 3088 bytes from the static space at 0x0x20100000\n6\nwriting 80052224 bytes from the dynamic space at 0x0x1000000000\n7\ndone]\n8\n%\nsrc git:(master) > ls -lh nlp-image\n9\n-rw-r--r--\n1 markw\nstaff\n76M Jul 13 12:49 nlp-image\nIn line 1 in this repl listing, I use the SBCL built-in function save-lisp-and-die to create the Lisp\nimage file. Using save-lisp-and-die is a great technique to use whenever it takes a while to set up\nyour work environment. Saving a Lisp image for use the next time you work on a Common Lisp\nproject is reminiscent of working in Smalltalk where your work is saved between sessions in an\nimage file.\nNote: I often use Clozure-CL (CCL) instead of SBCL for developing my NLP libraries because CCL\nloads my data files much faster than SBCL.\nYou can now start SBCL with the NLP library and data preloaded using the Lisp image that you just\ncreated:\n1\n%\nsrc git:(master) > sbcl --core nlp-image\n2\n* (in-package :kbnlp)\n3\n4\n#<PACKAGE \"KBNLP\">\n5\n* (defvar\n6\n*x*\n7\n(make-text-object\n8\n\"President Bob Smith talked to Congress about the economy and taxes\"))\n9\n\nNatural Language Processing\n115\n10\n*X*\n11\n12\n* *X*\n13\n14\n#S(TEXT\n15\n:URL \"\"\n16\n:TITLE \"\"\n17\n:SUMMARY \"<no summary>\"\n18\n:CATEGORY-TAGS ((\"news_politics.txt\" 0.01648)\n19\n(\"news_economy.txt\" 0.01601))\n20\n:KEY-WORDS NIL\n21\n:KEY-PHRASES NIL\n22\n:HUMAN-NAMES (\"President Bob Smith\")\n23\n:PLACE-NAMES NIL\n24\n:TEXT #(\"President\" \"Bob\" \"Smith\" \"talked\" \"to\" \"Congress\" \"about\" \"the\"\n25\n\"economy\" \"and\" \"taxes\")\n26\n:TAGS #(\"NNP\" \"NNP\" \"NNP\" \"VBD\" \"TO\" \"NNP\" \"IN\" \"DT\" \"NN\" \"CC\" \"NNS\")\n27\n:STEMS #(\"presid\" \"bob\" \"smith\" \"talk\" \"to\" \"congress\" \"about\" \"the\"\n28\n\"economi\" \"and\" \"tax\"))\n29\n*\nAt the end of the file src/knowledgebooks_nlp.lisp in comments is some test code that processes\nmuch more text so that a summary is also generated; here is a bit of the output you will see if you\nload the test code into your repl:\n1\n(:SUMMARY\n2\n\"Often those amendments are an effort to change government policy\n3\nby adding or subtracting money for carrying it out. The initial\n4\nsurge in foreclosures in 2007 and 2008 was tied to subprime\n5\nmortgages issued during the housing boom to people with shaky\n6\ncredit. 2 trillion in annual appropriations bills for funding\n7\nmost government programs — usually low profile legislation that\n8\ntypically dominates the work of the House in June and July.\n9\nBill Clinton said that banking in Europe is a good business.\n10\nThese days homeowners who got fixed rate prime mortgages because\n11\nthey had good credit cannot make their payments because they are\n12\nout of work. The question is whether or not the US dollar remains\n13\nthe world s reserve currency if not the US economy will face\n14\na depression.\"\n15\n:CATEGORY-TAGS ((\"news_politics.txt\" 0.38268)\n16\n(\"news_economy.txt\" 0.31182)\n17\n(\"news_war.txt\" 0.20174))\n\nNatural Language Processing\n116\n18\n:HUMAN-NAMES (\"President Bill Clinton\")\n19\n:PLACE-NAMES (\"Florida\"))\nThe top-level function make-text-object takes one required argument that can be either a string\ncontaining text or an array of strings where each string is a word or punctuation. Function make-\ntext-object has two optional keyword parameters: the URL where the text was found and a title.\n1\n(defun make-text-object (words &key (url \"\") (title \"\"))\n2\n(if (typep words 'string) (setq words (words-from-string words)))\n3\n(let* ((txt-obj (make-text :text words :url url :title title)))\n4\n(setf (text-tags txt-obj) (part-of-speech-tagger words))\n5\n(setf (text-stems txt-obj) (stem-text txt-obj))\n6\n;; note: we must find human and place names before calling\n7\n;; pronoun-resolution:\n8\n(let ((names-places (find-names-places txt-obj)))\n9\n(setf (text-human-names txt-obj) (car names-places))\n10\n(setf (text-place-names txt-obj) (cadr names-places)))\n11\n(setf (text-category-tags txt-obj)\n12\n(mapcar\n13\n#'(lambda (x)\n14\n(list\n15\n(car x)\n16\n(/ (cadr x) 1000000.0)))\n17\n(get-word-list-category (text-text txt-obj))))\n18\n(setf (text-summary txt-obj) (summarize txt-obj))\n19\ntxt-obj))\nIn line 2, we check if this function was called with a string containing text in which case the function\nwords-from-string is used to tokenize the text into an array of string tokens. Line two defines the\nlocal variable txt-obj with the value of a new text object with only three slots (attributes) defined:\ntext, url, and title. Line 4 sets the slot text-tags to the part of speech tokens using the function part-\nof-speech-tagger. We use the function find-names-places in line 8 to get person and place names\nand store these values in the text object. In lines 11 through 17 we use the function get-word-list-\ncategory to set the categories in the text object. In line 18 we similarly use the function summarize\nto calculate a summary of the text and also store it in the text object. We will discuss these NLP\nhelper functions throughout the rest of this chapter.\nThe function make-text-object returns a struct that is defined as:\n\nNatural Language Processing\n117\n(defstruct text\nurl\ntitle\nsummary\ncategory-tags\nkey-words\nkey-phrases\nhuman-names\nplace-names\ntext\ntags\nstems)\nPart of Speech Tagging\nThis tagger is the Common Lisp implementation of my FastTag open source project. I based this\nproject on Eric Brill’s PhD thesis (1995). He used machine learning on annotated text to learn tagging\nrules. I used a subset of the tagging rules that he generated that were most often used when he tested\nhis tagger. I hand coded his rules in Lisp (and Ruby, Java, and Pascal). My tagger is less accurate, but\nit is fast - thus the name FastTag.\nIf you just need part of speech tagging (and not summarization, categorization, and top level APIs\nused in the last section) you can load:\n1\n(ql:quickload \"fasttag\")\nYou can find the tagger implementation in the function part-of-speech-tagger. We already saw\nsample output from the tagger in the last section:\n1\n:TEXT #(\"President\" \"Bob\" \"Smith\" \"talked\" \"to\" \"Congress\" \"about\" \"the\"\n2\n\"economy\" \"and\" \"taxes\")\n3\n:TAGS #(\"NNP\" \"NNP\" \"NNP\" \"VBD\" \"TO\" \"NNP\" \"IN\" \"DT\" \"NN\" \"CC\" \"NNS\")\nThe following table shows the meanings of the tags and a few example words:\n\nNatural Language Processing\n118\nTag\nDefinition\nExample words\nCC\nCoord Conjuncn\nand, but, or\nNN\nNoun, sing. or mass\ndog\nCD\nCardinal number\none, two\nNNS\nNoun, plural\ndogs, cats\nDT\nDeterminer\nthe, some\nNNP\nProper noun, sing.\nEdinburgh\nEX\nExistential there\nthere\nNNPS\nProper noun, plural\nSmiths\nFW\nForeign Word\nmon dieu\nPDT\nPredeterminer\nall, both\nIN\nPreposition\nof, in, by\nPOS\nPossessive ending\n’s\nJJ\nAdjective\nbig\nPP\nPersonal pronoun\nI, you, she\nJJR\nAdj., comparative\nbigger\nPP$\nPossessive pronoun\nmy, one’s\nJJS\nAdj., superlative\nbiggest\nRB\nAdverb\nquickly\nLS\nList item marker\n1, One\nRBR\nAdverb, comparative\nfaster\nMD\nModal\ncan, should\nRBS\nAdverb, superlative\nfastest\nRP\nParticle\nup, off\nWP$\nPossessive-Wh\nwhose\nSYM\nSymbol\n+, %, &\nWRB\nWh-adverb\nhow, where\nTO\n“to”\nto\n$\nDollar sign\n$\nUH\nInterjection\noh, oops\n#\nPound sign\n#\nVB\nverb, base form\neat, run\n”\nquote\n”\nVBD\nverb, past tense\nate\nVBG\nverb, gerund\neating\n(\nLeft paren\n(\nVBN\nverb, past part\neaten\n)\nRight paren\n)\nVBP\nVerb, present\neat\n,\nComma\n,\nVBZ\nVerb, present\neats\n.\nSent-final punct\n. ! ?\nWDT\nWh-determiner\nwhich, that\n:\nMid-sent punct.\n: ; —\nWP\nWh pronoun\nwho, what\nThe function part-of-speech-tagger loops through all input words and initially assigns the most\n\nNatural Language Processing\n119\nlikely part of speech as specified in the lexicon. Then a subset of Brill’s rules are applied. Rules\noperate on the current word and the previous word.\nAs an example Common Lisp implementation of a rule, look for words that are tagged as common\nnouns, but end in “ing” so they should be a gerand (verb form):\n; rule 8: convert a common noun to a present\n;\nparticiple verb (i.e., a gerand)\n(if (equal (search \"NN\" r) 0)\n(let ((i (search \"ing\" w :from-end t)))\n(if (equal i (- (length w) 3))\n(setq r \"VBG\"))))\nYou can find the lexicon data in the file src/linguistic_data/FastTagData.lisp. This file is List code\ninstead of plain data (that in retrospect would be better because it would load faster) and looks like:\n(defvar lex-hash (make-hash-table :test #'equal :size 110000))\n(setf (gethash \"shakeup\" lex-hash) (list \"NN\"))\n(setf (gethash \"Laurance\" lex-hash) (list \"NNP\"))\n(setf (gethash \"expressing\" lex-hash) (list \"VBG\"))\n(setf (gethash \"citybred\" lex-hash) (list \"JJ\"))\n(setf (gethash \"negative\" lex-hash) (list \"JJ\" \"NN\"))\n(setf (gethash \"investors\" lex-hash) (list \"NNS\" \"NNPS\"))\n(setf (gethash \"founding\" lex-hash) (list \"NN\" \"VBG\" \"JJ\"))\nI generated this file automatically from lexicon data using a small Ruby script. Notice that words\ncan have more than one possible part of speech. The most common part of speech for a word is the\nfirst entry in the lexicon.\nCategorizing Text\nThe code to categorize text is fairly simple using a technique often called “bag of words.” I collected\nsample text in several different categories and for each category (like politics, sports, etc.) I calculated\nthe evidence or weight that words contribute to supporting a category. For example, the word\n“president” has a strong weight for the category “politics” but not for the category “sports.” The\nreason is that the word “president” occurs frequently in articles and books about politics. The data\nfile that contains the word weightings for each category is src/data/cat-data-tables.lisp. You can\nlook at this file; here is a very small part of it:\nIf you only need categorization and not the other libraries developed in this chapter, you can just\nload this library and run the example in the comment at the bottom of the file categorize_summa-\nrize.lisp:\n\nNatural Language Processing\n120\n({lang=”lisp”,linenos=off} (ql:quickload “categorize_summarize”) (defvar x “President Bill Clinton\n<<2 pages text no shown>> “) (defvar words1 (myutils:words-from-string x)) (print words1) (setq\ncats1 (categorize_summarize:categorize words1)) (print cats1) (defvar sum1 (categorize_summa-\nrize:summarize words1 cats1)) (print sum1)\nLet’s look at the implementation, starting with creating hash tables for storing word count data for\neach category or topic:\n;;;\nStarting topic: news_economy.txt\n(setf *h* (make-hash-table :test #'equal :size 1000))\n(setf (gethash \"news\" *h*) 3915)\n(setf (gethash \"debt\" *h*) 3826)\n(setf (gethash \"money\" *h*) 1809)\n(setf (gethash \"work\" *h*) 1779)\n(setf (gethash \"business\" *h*) 1631)\n(setf (gethash \"tax\" *h*) 1572)\n(setf (gethash \"poverty\" *h*) 1512)\nThis file was created by a simple Ruby script (not included with the book’s example code) that\nprocesses a list of sub-directories, one sub-directory per category. The following listing shows the\nimplementation of function get-word-list-category that calculates category tags for input text:\n1\n(defun get-word-list-category (words)\n2\n(let ((x nil)\n3\n(ss nil)\n4\n(cat-hash nil)\n5\n(word nil)\n6\n(len (length words))\n7\n(num-categories (length categoryHashtables))\n8\n(category-score-accumulation-array\n9\n(make-array num-categories :initial-element 0)))\n10\n11\n(defun list-sort (list-to-sort)\n12\n;;(pprint list-to-sort)\n13\n(sort list-to-sort\n14\n#'(lambda (list-element-1 list-element-2)\n15\n(> (cadr list-element-1) (cadr list-element-2)))))\n16\n17\n(do ((k 0 (+ k 1)))\n18\n((equal k len))\n19\n(setf word (string-downcase (aref words k)))\n\nNatural Language Processing\n121\n20\n(do ((i 0 (+ i 1)))\n21\n((equal i num-categories))\n22\n(setf cat-hash (nth i categoryHashtables))\n23\n(setf x (gethash word cat-hash))\n24\n(if x\n25\n(setf\n26\n(aref category-score-accumulation-array i)\n27\n(+ x (aref category-score-accumulation-array i))))))\n28\n(setf ss '())\n29\n(do ((i 0 (+ i 1)))\n30\n((equal i num-categories))\n31\n(if (> (aref category-score-accumulation-array i) 0.01)\n32\n(setf\n33\nss\n34\n(cons\n35\n(list\n36\n(nth i categoryNames)\n37\n(round (* (aref category-score-accumulation-array i) 10)))\n38\nss))))\n39\n(setf ss (list-sort ss))\n40\n(let ((cutoff (/ (cadar ss) 2))\n41\n(results-array '()))\n42\n(dolist (hit ss)\n43\n(if (> (cadr hit) cutoff)\n44\n(setf results-array (cons hit results-array))))\n45\n(reverse results-array))))\nOn thing to notice in this listing is lines 11 through 15 where I define a nested function list-sort that\ntakes a list of sub-lists and sorts the sublists based on the second value (which is a number) in the\nsublists. I often nest functions when the “inner” functions are only used in the “outer” function.\nLines 2 through 9 define several local variables used in the outer function. The global variable\ncategoryHashtables is a list of word weighting score hash tables, one for each category. The local\nvariable category-score-accumulation-array is initialized to an array containing the number zero\nin each element and will be used to “keep score” of each category. The highest scored categories will\nbe the return value for the outer function.\nLines 17 through 27 are two nested loops. The outer loop is over each word in the input word array.\nThe inner loop is over the number of categories. The logic is simple: for each word check to see if it\nhas a weighting score in each category’s word weighting score hash table and if it is, increment the\nmatching category’s score.\nThe local variable ss is set to an empty list on line 28 and in the loop in lines 29 through 38 I am\ncopying over categories and their scores when the score is over a threshold value of 0.01. We sort\n\nNatural Language Processing\n122\nthe list in ss on line 39 using the inner function and then return the categories with a score greater\nthan the median category score.\nDetecting People’s Names and Place Names\nThe code for detecting people and place names is in the top level API code in the package defined\nin src/kbnlp. This package is loaded using:\n(ql:quickload \"kbnlp\")\n(kbnlp:make-text-object \"President Bill Clinton ran for president of the USA\")\nThe functions that support identifying people’s names and place names in text are in the Common\nLisp package kb nlp::\n• find-names (words tags exclusion-list) – words is an array of strings for the words in text, tags\nare the parts of speech tags (from FastTag), and the exclusion list is a an array of words that\nyou want to exclude from being considered as parts of people’s names. The list of found names\nrecords starting and stopping indices for names in the array words.\n• not-in-list-find-names-helper (a-list start end) – returns true if a found name is not already\nbeen added to a list for saving people’s names in text\n• find-places (words exclusion-list) – this is similar to find-names, but it finds place names. The\nlist of found place names records starting and stopping indices for place names in the array\nwords.\n• not-in-list-find-places-helper (a-list start end) – returns true if a found place name is not already\nbeen added to a list for saving place names in text\n• build-list-find-name-helper (v indices) – This converts lists of start/stop word indices to strings\ncontaining the names\n• find-names-places (txt-object) – this is the top level function that your application will call. It\ntakes a defstruct text object as input and modifies the defstruct text by adding people’s and\nplace names it finds in the text. You saw an example of this earlier in this chapter.\nI will let you read the code and just list the top level function:\n\nNatural Language Processing\n123\n1\n(defun find-names-places (txt-object)\n2\n(let* ((words (text-text txt-object))\n3\n(tags (text-tags txt-object))\n4\n(place-indices (find-places words nil))\n5\n(name-indices (find-names words tags place-indices))\n6\n(name-list\n7\n(remove-duplicates\n8\n(build-list-find-name-helper words name-indices) :test #'equal))\n9\n(place-list\n10\n(remove-duplicates\n11\n(build-list-find-name-helper words place-indices) :test #'equal)))\n12\n(let ((ret '()))\n13\n(dolist (x name-list)\n14\n(if (search \" \" x)\n15\n(setq ret (cons x ret))))\n16\n(setq name-list (reverse ret)))\n17\n(list\n18\n(remove-shorter-names name-list)\n19\n(remove-shorter-names place-list))))\nIn line 2 we are using the slot accessor text-text to fetch the array of word tokens from the text\nobject. In lines 3, 4, and 5 we are doing the same for part of speech tags, place name indices in the\nwords array, and person names indices in the words array.\nIn lines 6 through 11 we are using the function build-list-find-name-helper twice to construct the\nperson names and place names as strings given the indices in the words array. We are also using the\nCommon Lisp built-in function remove-duplicates to get rid of duplicate names.\nIn lines 12 through 16 we are discarding any persons names that do not contain a space, that is, only\nkeep names that are at least two word tokens. Lines 17 through 19 define the return value for the\nfunction: a list of lists of people and place names using the function remove-shorter-names twice\nto remove shorter versions of the same names from the lists. For example, if we had two names “Mr.\nJohn Smith” and “John Smith” then we would want to drop the shorter name “John Smith” from the\nreturn list.\nSummarizing Text\nThe code for summarizing text is located in the directory src/categorize_summarize and can be\nloaded using:\n({lang=”lisp”,linenos=off} (ql:quickload “categorize_summarize”)\nThe code for summarization depends on the categorization code we saw earlier.\n\nNatural Language Processing\n124\nThere are many applications for summarizing text. As an example, if you are writing a document\nmanagement system you will certainly want to use something like Solr to provide search func-\ntionality. Solr will return highlighted matches in snippets of indexed document field values. Using\nsummarization, when you add documents to a Solr (or other) search index you could create a new\nunindexed field that contains a document summary. Then when the users of your system see search\nresults they will see the type of highlighted matches in snippets they are used to seeing in Google,\nBing, or DuckDuckGo search results, and, they will see a summary of the document.\nSounds good? The problem to solve is getting good summaries of text and the technique used may\nhave to be modified depending on the type of text you are trying to summarize. There are two basic\ntechniques for summarization: a practical way that almost everyone uses, and an area of research\nthat I believe has so far seen little practical application. The techniques are sentence extraction and\nabstraction of text into a shorter form by combining and altering sentences. We will use sentence\nextraction.\nHow do we choose which sentences in text to extract for the summary? The idea I had in 1999 was\nsimple. Since I usually categorize text in my NLP processing pipeline why not use the words that\ngave the strongest evidence for categorizing text, and find the sentences with the largest number of\nthese words. As a concrete example, if I categorize text as being “politics”, I identify the words in the\ntext like “president”, “congress”, “election”, etc. that triggered the “politics” classification, and find\nthe sentences with the largest concentrations of these words.\nSummarization is something that you will probably need to experiment with depending on your\napplication. My old summarization code contained a lot of special cases, blocks of commented out\ncode, etc. I have attempted to shorten and simplify my old summarization code for the purposes of\nthis book as much as possible and still maintain useful functionality.\nThe function for summarizing text is fairly simple because when the function summarize is called\nby the top level NLP library function make-text-object, the input text has already been categorized.\nRemember from the example at the beginning of the chapter that the category data looks like this:\n1\n:CATEGORY-TAGS ((\"news_politics.txt\" 0.38268)\n2\n(\"news_economy.txt\" 0.31182)\n3\n(\"news_war.txt\" 0.20174))\nThis category data is saved in the local variable cats on line 4 of the following listing.\n\nNatural Language Processing\n125\n1\n(defun summarize (txt-obj)\n2\n(let* ((words (text-text txt-obj))\n3\n(num-words (length words))\n4\n(cats (text-category-tags txt-obj))\n5\n(sentence-count 0)\n6\nbest-sentences sentence (score 0))\n7\n;; loop over sentences:\n8\n(dotimes (i num-words)\n9\n(let ((word (svref words i)))\n10\n(dolist (cat cats)\n11\n(let* ((hash (gethash (car cat) categoryToHash))\n12\n(value (gethash word hash)))\n13\n(if value\n14\n(setq score (+ score (* 0.01 value (cadr cat)))))))\n15\n(push word sentence)\n16\n(if (or (equal word \".\") (equal word \"!\") (equal word \";\"))\n17\n(let ()\n18\n(setq sentence (reverse sentence))\n19\n(setq score (/ score (1+ (length sentence))))\n20\n(setq sentence-count (1+ sentence-count))\n21\n(format t \"~%~A : ~A~%\" sentence score)\n22\n;; process this sentence:\n23\n(if (and\n24\n(> score 0.4)\n25\n(> (length sentence) 4)\n26\n(< (length sentence) 30))\n27\n(progn\n28\n(setq sentence\n29\n(reduce\n30\n#'(lambda (x y) (concatenate 'string x \" \" y))\n31\n(coerce sentence 'list)))\n32\n(push (list sentence score) best-sentences)))\n33\n(setf sentence nil score 0)))))\n34\n(setf\n35\nbest-sentences\n36\n(sort\n37\nbest-sentences\n38\n#'(lambda (x y) (> (cadr x) (cadr y)))))\n39\n(if best-sentences\n40\n(replace-all\n41\n(reduce #'(lambda (x y) (concatenate 'string x \" \" y))\n42\n(mapcar #'(lambda (x) (car x)) best-sentences))\n43\n\" .\" \".\")\n\nNatural Language Processing\n126\n44\n\"<no summary>\")))\nThe nested loops in lines 8 through 33 look a little complicated, so let’s walk through it. Our goal\nis to calculate an importance score for each word token in the input text and to then select a few\nsentences containing highly scored words. The outer loop is over the word tokens in the input text.\nFor each word token we loop over the list of categories, looking up the current word in each category\nhash and incrementing the score for the current word token. As we increment the word token scores\nwe also look for sentence breaks and save sentences.\nThe complicated bit of code in lines 16 through 32 where I construct sentences and their scores, and\nstore sentences with a score above a threshold value in the list best-sentences. After the two nested\nloops, in lines 34 through 44 we simply sort the sentences by score and select the “best” sentences for\nthe summary. The extracted sentences are no longer in their original order, which can have strange\neffects, but I like seeing the most relevant sentences first.\nText Mining\nText mining in general refers to finding data in unstructured text. We have covered several text\nmining techniques in this chapter:\n• Named entity recognition - the NLP library covered in this chapter recognizes person\nand place entity names. I leave it as an exercise for you to extend this library to handle\ncompany and product names. You can start by collecting company and product names in\nthe files src/kbnlp/linguistic_data/names/names.companies and src/kbnlp/data/names/-\nnames.products and extend the library code.\n• Categorizing text - you can increase the accuracy of categorization by adding more weighted\nwords/terms that support categories. If you are already using Java in the systems you build, I\nrecommend the Apache OpenNLP library that is more accurate than the simpler “bag of words”\napproach I used in my Common Lisp NLP library. If you use Python, then I recommend that\nyou also try the NLTK library.\n• Summarizing text.\nIn the next chapter I am going to cover another “data centric” topic: performing information\ngathering on the web. You will likely find some synergy between being able to use NLP to create\nstructured data from unstructured text.\n\nInformation Gathering\nThis chapter covers information gathering on the web using data sources and general techniques that\nI have found useful. When I was planning this new book edition I had intended to also cover some\nbasics for using the Semantic Web from Common Lisp, basically distilling some of the data from\nmy previous book “Practical Semantic Web and Linked Data Applications, Common Lisp Edition”\npublished in 2011. However since a free PDF is now available for that book⁵⁵ I decided to just refer\nyou to my previous work if you are interested in the Semantic Web and Linked Data. You can also\nfind the Java edition of this previous book on my web site.\nGathering information from the web in realtime has some real advantages:\n• You don’t need to worry about storing data locally.\n• Information is up to date (depending on which web data resources you choose to use).\nThere are also a few things to consider:\n• Data on the web may have legal restrictions on its use so be sure to read the terms and\nconditions on web sites that you would like to use.\n• Authorship and validity of data may be questionable.\nDBPedia Lookup Service\nWikipedia is a great source of information. As you may know, you can download a data dump of all\nWikipedia data⁵⁶ with or without version information and comments. When I want fast access to the\nentire Wikipedia set of English language articles I choose the second option and just get the current\npages with no comments of versioning information. This is the direct download link for current\nWikipedia articles.⁵⁷ There are no comments or user pages in this GZIP file. This is not as much data\nas you might think, only about 9 gigabytes compressed or about 42 gigabytes uncompressed.\nTo load and run an example, try:\n(ql:quickload \"dbpedia\")\n(dbpedia:dbpedia-lookup \"berlin\")\nWikipedia is a great resource to have on hand but I am going to show you in this section how to\naccess the Semantic Web version or Wikipedia, DBPedia⁵⁸ using the DBPedia Lookup Service in the\nnext code listing that shows the contents of the example file dbpedia-lookup.lisp in the directory\nsrc/dbpedia:\n⁵⁵http://markwatson.com/#books/\n⁵⁶https://en.wikipedia.org/wiki/Wikipedia:Database_download\n⁵⁷http://download.wikimedia.org/enwiki/latest/enwiki-latest-pages-articles.xml.bz2\n⁵⁸http://dbpedia.org/\n\nInformation Gathering\n128\n1\n(ql:quickload :drakma)\n2\n(ql:quickload :babel)\n3\n(ql:quickload :s-xml)\n4\n5\n;; utility from http://cl-cookbook.sourceforge.net/strings.html#manip:\n6\n(defun replace-all (string part replacement &key (test #'char=))\n7\n\"Returns a new string in which all the occurrences of the part\n8\nis replaced with replacement.\"\n9\n(with-output-to-string (out)\n10\n(loop with part-length = (length part)\n11\nfor old-pos = 0 then (+ pos part-length)\n12\nfor pos = (search part string\n13\n:start2 old-pos\n14\n:test test)\n15\ndo (write-string string out\n16\n:start old-pos\n17\n:end (or pos (length string)))\n18\nwhen pos do (write-string replacement out)\n19\nwhile pos)))\n20\n21\n(defstruct dbpedia-data uri label description)\n22\n23\n(defun dbpedia-lookup (search-string)\n24\n(let* ((s-str (replace-all search-string \" \" \"+\"))\n25\n(s-uri\n26\n(concatenate\n27\n'string\n28\n\"http://lookup.dbpedia.org/api/search.asmx/KeywordSearch?QueryString=\"\n29\ns-str))\n30\n(response-body nil)\n31\n(response-status nil)\n32\n(response-headers nil)\n33\n(xml nil)\n34\nret)\n35\n(multiple-value-setq\n36\n(response-body response-status response-headers)\n37\n(drakma:http-request\n38\ns-uri\n39\n:method :get\n40\n:accept \"application/xml\"))\n41\n;; (print (list \"raw response body as XML:\" response-body))\n42\n;;(print (list (\"status:\" response-status \"headers:\" response-headers)))\n43\n(setf xml\n\nInformation Gathering\n129\n44\n(s-xml:parse-xml-string\n45\n(babel:octets-to-string response-body)))\n46\n(dolist (r (cdr xml))\n47\n;; assumption: data is returned in the order:\n48\n;;\n1. label\n49\n;;\n2. DBPedia URI for more information\n50\n;;\n3. description\n51\n(push\n52\n(make-dbpedia-data\n53\n:uri (cadr (nth 2 r))\n54\n:label (cadr (nth 1 r))\n55\n:description\n56\n(string-trim\n57\n'(#\\Space #\\NewLine #\\Tab)\n58\n(cadr (nth 3 r))))\n59\nret))\n60\n(reverse ret)))\n61\n62\n;; (dbpedia-lookup \"berlin\")\nI am only capturing the attributes for DBPedia URI, label and description in this example code. If\nyou uncomment line 41 and look at the entire response body from the call to DBPedia Lookup, you\ncan see other attributes that you might want to capture in your applications.\nHere is a sample call to the function dbpedia:dbpedia-lookup (only some of the returned data is\nshown):\n1\n* (ql:quickload \"dbpedia\")\n2\n* (dbpedia:dbpedia-lookup \"berlin\")\n3\n4\n(#S(DBPEDIA-DATA\n5\n:URI \"http://dbpedia.org/resource/Berlin\"\n6\n:LABEL \"Berlin\"\n7\n:DESCRIPTION\n8\n\"Berlin is the capital city of Germany and one of the 16 states of Germany.\n9\nWith a population of 3.5 million people, Berlin is Germany's largest city\n10\nand is the second most populous city proper and the eighth most populous\n11\nurban area in the European Union. Located in northeastern Germany, it is\n12\nthe center of the Berlin-Brandenburg Metropolitan Region, which has 5.9\n13\nmillion residents from over 190 nations. Located in the European Plains,\n14\nBerlin is influenced by a temperate seasonal climate.\")\n15\n...)\nWikipedia, and the DBPedia linked data for of Wikipedia are great sources of online data. If you\n\nInformation Gathering\n130\nget creative, you will be able to think of ways to modify the systems you build to pull data from\nDPPedia. One warning: Semantic Web/Linked Data sources on the web are not available 100% of\nthe time. If your business applications depend on having the DBPedia always available then you can\nfollow the instructions on the DBPedia web site⁵⁹ to install the service on one of your own servers.\nWeb Spiders\nWhen you write web spiders to collect data from the web there are two things to consider:\n• Make sure you read the terms of service for web sites whose data you want to use. I have found\nthat calling or emailing web site owners explaining how I want to use the data on their site\nusually works to get permission.\n• Make sure you don’t access a site too quickly. It is polite to wait a second or two between\nfetching pages and other assets from a web site.\nWe have already used the Drakma web client library in this book. See the files src/dbpedia/dbpedia-\nlookup.lisp (covered in the last section) and src/solr_examples/solr-client.lisp (covered in the\nChapter on NoSQL). Paul Nathan has written library using Drakma to crawl a web site with an\nexample to print out links as they are found. His code is available under the AGPL license at\narticulate-lisp.com/src/web-trotter.lisp⁶⁰ and I recommend that as a starting point.\nI find it is sometimes easier during development to make local copies of a web site so that I don’t\nhave to use excess resources from web site hosts. Assuming that you have the wget utility installed,\nyou can mirror a site like this:\n1\nwget -m -w 2 http://knowledgebooks.com/\n2\nwget -mk -w 2 http://knowledgebooks.com/\nBoth of these examples have a two-second delay between HTTP requests for resources. The option\n-m indicates to recursively follow all links on the web site. The -w 2 option delays for two seconds\nbetween requests. The option -mk converts URI references to local file references on your local\nmirror. The second example on line 2 is more convenient.\nWe covered reading from local files in the Chapter on Input and Output. One trick I use is to simply\nconcatenate all web pages into one file. Assuming that you created a local mirror of a web site, cd\nto the top level directory and use something like this:\n1\ncd knowledgebooks.com\n2\ncat *.html */*.html > ../web_site.html\nYou can then open the file, search for text in in p, div, h1, etc. HTML elements to process an entire\nweb site as one file.\n⁵⁹http://dbpedia.org\n⁶⁰http://articulate-lisp.com/examples/trotter.html\n\nInformation Gathering\n131\nUsing Apache Nutch\nApache Nutch⁶¹, like Solr, is built on Lucene search technology. I use Nutch as a “search engine in a\nbox” when I need to spider web sites and I want a local copy with a good search index.\nNutch handles a different developer’s use case over Solr which we covered in the Chapter on NoSQL.\nAs we saw, Solr is an effective tool for indexing and searching structured data as documents. With\nvery little setup, Nutch can be set up to automatically keep an up to date index of a list of web sites,\nand optionally follow links to some desired depth from these “seed” web sites.\nYou can use the same Common Lisp client code that we used for Solr with one exception; you will\nneed to change the root URI for the search service to:\n1\nhttp://localhost:8080/opensearch?query=\nSo the modified client code src/solr_examples/solr-client.lisp needs one line changed:\n1\n(defun do-search (&rest terms)\n2\n(let ((query-string (format nil \"~{~A~^+AND+~}\" terms)))\n3\n(cl-json:decode-json-from-string\n4\n(drakma:http-request\n5\n(concatenate\n6\n'string\n7\n\"http://localhost:8080/opensearch?query=\"\n8\nquery-string\n9\n\"&wt=json\")))))\nEarly versions of Nutch were very simple to install and configure. Later versions of Nutch have been\nmore complex, more performant, and have more services, but it will take you longer to get set up\nthan earlier versions. If you just want to experiment with Nutch, you might want to start with an\nearlier version.\nThe OpenSearch.org⁶² web site contains many public OpenSearch services that you might want to\ntry. If you want to modify the example client code in src/solr-client.lisp a good start is OpenSearch\nservices that return JSON data and OpenSearch Community JSON formats web page⁶³ is a good\nplace to start. Some of the services on this web page like the New York Times service require that\nyou sign up for a developer’s API key.\nWhen I start writing an application that requires web data (no matter which programming language\nI am using) I start by finding services that may provide the type of data I need and do my initial\ndevelopment with a web browser with plugin support to nicely format XML and JSON data. I do a\nlot of exploring and take a lot of notes before I write any code.\n⁶¹https://nutch.apache.org/\n⁶²http://www.opensearch.org/Home\n⁶³http://www.opensearch.org/Community/JSON_Formats\n\nInformation Gathering\n132\nWrap Up\nI tried to provide some examples and advice in this short chapter to show you that even though\nother languages like Ruby and Python have more libraries and tools for gathering information from\nthe web, Common Lisp has good libraries for information gathering also and they are easily used\nvia Quicklisp.\n\nUsing The CL Machine-Learning\nLibrary\nThe CL Machine-Learning (CLML) library was originally developed by MSI (NTT DATA Mathemat-\nical Systems Inc. in Japan) and is supported by many developers. You should visit the CLML web\npage⁶⁴ for project documentation and follow the installation directions and read about the project\nbefore using the examples in this chapter. However if you just want to quickly try the following\nCLML examples then you can install CLML using Quicklisp:\n1\nmkdir -p ~/quicklisp/local-projects\n2\ncd ~/quicklisp/local-projects\n3\ngit clone https://github.com/mmaul/clml.git\n4\nsbcl --dynamic-space-size 2560\n5\n> (ql:quickload :clml :verbose t)\nThe installation will take a while to run but after installation using the libraries via quickload is fast.\nYou can now run the example Quicklisp project src/clml_examples:\n$ sbcl --dynamic-space-size 2560\n* (ql:quickload \"clmltest\")\n* (clmltest:clml-tests-example)\nPlease be patient the first time you run this because the first time you load the example project, the\none time installation of CLML will take a while to run but after installation then the example project\nloads quickly. CLML installation involves downloading and installing BLAS, LAPACK, and other\nlibraries.\nOther resources for CLML are the tutorials⁶⁵ and contributed extensions⁶⁶ that include support for\nplotting (using several libraries) and for fetching data sets.\nAlthough CLML is fairly portable we will be using SBCL and we need to increase the heap space\nwhen starting SBCL when we want to use the CLML library:\nsbcl --dynamic-space-size 5000\n⁶⁴https://github.com/mmaul/clml\n⁶⁵https://github.com/mmaul/clml.tutorials\n⁶⁶https://github.com/mmaul/clml.extras\n\nUsing The CL Machine-Learning Library\n134\nYou can refer to the documentation at https://github.com/mmaul/clml⁶⁷. This documentation lists\nthe packages with some information for each package but realistically I keep the source code for\nCLML in an editor or IDE and read source code while writing code that uses CLML. I will show you\nwith short examples how to use the KNN (K nearest neighbors) and SVM (support vector machines)\nAPIs. We will not cover other useful CLML APIs like time series processing, Naive Bayes, PCA\n(principle component analysis) and general matrix and tensor operations.\nEven though the learning curve is a bit steep, CLML provides a lot of functionality for machine\nlearning, dealing with time series data, and general matrix and tensor operations.\nUsing the CLML Data Loading and Access APIs\nThe CLML project uses several data sets and since the few that we will use are small files, they are\nincluded in the book’s repository in directory machine_learning_data under the src directory. The\nfirst few lines of labeled_cancer_training_data.csv are:\nCl.thickness,Cell.size,Cell.shape,Marg.adhesion,Epith.c.size,Bare.nuclei,Bl.cromatin\\\n,Normal.nucleoli,Mitoses,Class\n5,4,4,5,7,10,3,2,1,benign\n6,8,8,1,3,4,3,7,1,benign\n8,10,10,8,7,10,9,7,1,malignant\n2,1,2,1,2,1,3,1,1,benign\nThe first line in the CSV data files specifies names for each attribute with the name of the last\ncolumn being “Class” which here takes on values benign or malignant. Later, the goal will be to\ncreate models that are constructed from training data and then make predictions of the “Class” of\nnew input data. We will look at how to build and use machine learning models later but here we\nconcentrate on reading and using input data.\nThe example file clml_data_apis.lisp shows how to open a file and loop over the values for each\nrow:\n1\n;; note; run SBCL using: sbcl --dynamic-space-size 2560\n2\n3\n(ql:quickload '(:clml\n4\n:clml.hjs)) ; read data sets\n5\n6\n(defpackage #:clml-data-test\n7\n(:use #:cl #:clml.hjs.read-data))\n8\n9\n(in-package #:clml-data-test)\n⁶⁷https://github.com/mmaul/clml\n\nUsing The CL Machine-Learning Library\n135\n10\n11\n(defun read-data ()\n12\n(let ((train1\n13\n(clml.hjs.read-data:read-data-from-file\n14\n\"./machine_learning_data/labeled_cancer_training_data.csv\"\n15\n:type :csv\n16\n:csv-type-spec (append\n17\n(make-list 9 :initial-element 'double-float)\n18\n'(symbol)))))\n19\n(loop-over-and-print-data train1)))\n20\n21\n(defun loop-over-and-print-data (clml-data-set)\n22\n(print \"Loop over and print a CLML data set:\")\n23\n(let ((testdata (clml.hjs.read-data:dataset-points clml-data-set)))\n24\n(loop for td across testdata\n25\ndo\n26\n(print td))))\n27\n28\n(read-data)\nThe function read-data defined in lines 11-19 uses the utility function clml.hjs.read-data:read-\ndata-from-file to read a CSV (comma separated value) spreadsheet file from disk. The CSV file is\nexpected to contain 10 columns (set in lines 17-18) with the first nine columns containing floating\npoint values and the last column text data.\nThe function loop-over-and-print-data defined in lines 21-26 reads the CLML data set object,\nlooping over each data sample (i.e., each row in the original spreadsheet file) and printing it.\nHere is some output from loading this file:\n1\n$ sbcl --dynamic-space-size 2560\n2\nThis is SBCL 1.3.16, an implementation of ANSI Common Lisp.\n3\nMore information about SBCL is available at <http://www.sbcl.org/>.\n4\n5\nSBCL is free software, provided as is, with absolutely no warranty.\n6\nIt is mostly in the public domain; some portions are provided under\n7\nBSD-style licenses.\nSee the CREDITS and COPYING files in the\n8\ndistribution for more information.\n9\n* (load \"clml_data_apis.lisp\")\n10\n11\n\"Loop over and print a CLML data set:\"\n12\n#(5.0d0 4.0d0 4.0d0 5.0d0 7.0d0 10.0d0 3.0d0 2.0d0 1.0d0 |benign|)\n13\n#(6.0d0 8.0d0 8.0d0 1.0d0 3.0d0 4.0d0 3.0d0 7.0d0 1.0d0 |benign|)\n\nUsing The CL Machine-Learning Library\n136\n14\n#(8.0d0 10.0d0 10.0d0 8.0d0 7.0d0 10.0d0 9.0d0 7.0d0 1.0d0 |malignant|)\n15\n#(2.0d0 1.0d0 2.0d0 1.0d0 2.0d0 1.0d0 3.0d0 1.0d0 1.0d0 |benign|)\nIn the next section we will use the same cancer data training file, and another test data in the same\nformat to cluster this cancer data into similar sets, one set for non-malignant and one for malignant\nsamples.\nK-Means Clustering of Cancer Data Set\nWe will now read the same University of Wisconsin cancer data set and cluster the input samples\n(one sample per row of the spreadsheet file) into similar classes. We will find after training a model\nthat the data is separated into two clusters, representing non-malignant and malignant samples.\nThe function cancer-data-cluster-example-read-data defined in lines 33-47 is very similar to the\nfunction read-data in the last section except here we read in two data files: one for training and one\nfor testing.\nThe function cluster-using-k-nn defined in lines 13-30 uses the training and test data objects to first\ntrain a model and then to test it with test data that was previously used for training. Notice how\nwe call this function in line 47: the first two arguments are the two data set objects, the third is the\nstring “Class” that is the label for the 10th column of the original spreadsheet CSV files, and the last\nargument is the type of distance measurement used to compare two data samples (i.e., comparing\nany two rows of the training CSV data file).\n1\n;; note; run SBCL using: sbcl --dynamic-space-size 2560\n2\n3\n(ql:quickload '(:clml\n4\n:clml.hjs ; utilities\n5\n:clml.clustering))\n6\n7\n(defpackage #:clml-knn-cluster-example1\n8\n(:use #:cl #:clml.hjs.read-data))\n9\n10\n(in-package #:clml-knn-cluster-example1)\n11\n12\n;; folowing is derived from test code in CLML:\n13\n(defun cluster-using-k-nn (test train objective-param-name\nmanhattan)\n14\n(let (original-data-column-length)\n15\n(setq original-data-column-length\n16\n(length (aref (clml.hjs.read-data:dataset-points train) 0)))\n17\n(let* ((k 5)\n18\n(k-nn-estimator\n19\n(clml.nearest-search.k-nn:k-nn-analyze train\n\nUsing The CL Machine-Learning Library\n137\n20\nk\n21\nobjective-param-name :all\n22\n:distance manhattan :normalize t)))\n23\n(loop for data across\n24\n(dataset-points\n25\n(clml.nearest-search.k-nn:k-nn-estimate k-nn-estimator test))\n26\nif (equal (aref data 0) (aref data original-data-column-length))\n27\ndo\n28\n(format t \"Correct: ~a~%\" data)\n29\nelse do\n30\n(format t \"Wrong:\n~a~%\" data)))))\n31\n32\n;; folowing is derived from test code in CLML:\n33\n(defun cancer-data-cluster-example-read-data ()\n34\n(let ((train1\n35\n(clml.hjs.read-data:read-data-from-file\n36\n\"./machine_learning_data/labeled_cancer_training_data.csv\"\n37\n:type :csv\n38\n:csv-type-spec (append (make-list 9 :initial-element 'double-float)\n39\n'(symbol))))\n40\n(test1\n41\n(clml.hjs.read-data:read-data-from-file\n42\n\"./machine_learning_data/labeled_cancer_test_data.csv\"\n43\n:type :csv\n44\n:csv-type-spec (append (make-list 9 :initial-element 'double-float)\n45\n'(symbol)))))\n46\n;;(print test1)\n47\n(print (cluster-using-k-nn test1 train1 \"Class\" :double-manhattan))))\n48\n49\n(cancer-data-cluster-example-read-data)\nThe following listing shows the output from running the last code example:\n1\nNumber of self-misjudgement : 13\n2\nCorrect: #(benign 5.0d0 1.0d0 1.0d0 1.0d0 2.0d0 1.0d0 3.0d0 1.0d0 1.0d0 benign)\n3\nCorrect: #(benign 3.0d0 1.0d0 1.0d0 1.0d0 2.0d0 2.0d0 3.0d0 1.0d0 1.0d0 benign)\n4\nCorrect: #(benign 4.0d0 1.0d0 1.0d0 3.0d0 2.0d0 1.0d0 3.0d0 1.0d0 1.0d0 benign)\n5\nCorrect: #(benign 1.0d0 1.0d0 1.0d0 1.0d0 2.0d0 10.0d0 3.0d0 1.0d0 1.0d0 benign)\n6\nCorrect: #(benign 2.0d0 1.0d0 1.0d0 1.0d0 2.0d0 1.0d0 1.0d0 1.0d0 5.0d0 benign)\n7\nCorrect: #(benign 1.0d0 1.0d0 1.0d0 1.0d0 1.0d0 1.0d0 3.0d0 1.0d0 1.0d0 benign)\n8\nWrong:\n#(benign 5.0d0 3.0d0 3.0d0 3.0d0 2.0d0 3.0d0 4.0d0 4.0d0 1.0d0\n9\nmalignant)\n10\nCorrect: #(malignant 8.0d0 7.0d0 5.0d0 10.0d0 7.0d0 9.0d0 5.0d0 5.0d0 4.0d0\n\nUsing The CL Machine-Learning Library\n138\n11\nmalignant)\n12\nCorrect: #(benign 4.0d0 1.0d0 1.0d0 1.0d0 2.0d0 1.0d0 2.0d0 1.0d0 1.0d0 benign)\n13\nCorrect: #(malignant 10.0d0 7.0d0 7.0d0 6.0d0 4.0d0 10.0d0 4.0d0 1.0d0 2.0d0\n14\nmalignant)\n15\n...\nSVM Classification of Cancer Data Set\nWe will now reuse the same cancer data set but use a different way to classify data into non-\nmalignant and malignant categories: Support Vector Machines (SVM). SVMs are linear classifiers\nwhich means that they work best when data is linearly separable. In the case of the cancer data, there\nare nine dimensions of values that (hopefully) predict one of the two output classes (or categories).\nIf we think of the first 9 columns of data as defining a 9-dimensional space, then SVM will work well\nwhen a 8-dimensional hyperplane separates the samples into the two output classes (categories).\nTo make this simpler to visualize, if we just had two input columns, that defines a two-dimensional\nspace, and if a straight line can separate most of the examples into the two output categories, then\nthe data is linearly separable so SVM is a good technique to use. The SVM algorithm is effectively\ndetermining the parameters defining this one-dimensional line (or in the cancer data case, the 9-\ndimensional hyperspace).\nWhat if data is not linearly separable? Then use the backpropagation neural network code in the\nchapter “Backpropagation Neural Networks” or the deep learning code in the chapter “Using Armed\nBear Common Lisp With DeepLearning4j” to create a model.\nSVM is very efficient so it often makes sense to first try SVM and if trained models are not accurate\nenough then use neural networks, including deep learning.\nThe following listing of file clml_svm_classifier.lisp shows how to read data, build a model and\nevaluate the model with different test data. In line 15 we use the function clml.svm.mu:svm that\nrequires the type of kernel function to use, the training data, and testing data. Just for reference, we\nusually use Gaussian kernel functions for processing numeric data and linear kernel functions for\nhandling text in natural language processing applications. Here we use a Gaussian kernel.\nThe function cancer-data-svm-example-read-data defined on line 40 differs from how we read and\nprocessed data earlier because we need to separate out the positive and negative training examples.\nThe data is split in the lexically scoped function in lines 42-52. The last block of code in lines 54-82\nis just top-level test code that gets executed when the file clml_svm_classifier.lisp is loaded.\n\nUsing The CL Machine-Learning Library\n139\n1\n;; note; run SBCL using: sbcl --dynamic-space-size 2560\n2\n3\n(ql:quickload '(:clml\n4\n:clml.hjs ; utilities\n5\n:clml.svm))\n6\n7\n(defpackage #:clml-svm-classifier-example1\n8\n(:use #:cl #:clml.hjs.read-data))\n9\n10\n(in-package #:clml-svm-classifier-example1)\n11\n12\n(defun svm-classifier-test (kernel train test)\n13\n\"train and test are lists of lists, with first elements being negative\n14\nsamples and the second elements being positive samples\"\n15\n(let ((decision-function (clml.svm.mu:svm kernel (cadr train) (car train)))\n16\n(correct-positives 0)\n17\n(wrong-positives 0)\n18\n(correct-negatives 0)\n19\n(wrong-negatives 0))\n20\n;; type: #<CLOSURE (LAMBDA (CLML.SVM.MU::Z) :IN CLML.SVM.MU::DECISION)>\n21\n(print decision-function)\n22\n(princ \"***** NEGATIVE TESTS: calling decision function:\")\n23\n(terpri)\n24\n(dolist (neg (car test))\n;; negative test examples\n25\n(let ((prediction (funcall decision-function neg)))\n26\n(print prediction)\n27\n(if prediction (incf wrong-negatives) (incf correct-negatives))))\n28\n(princ \"***** POSITIVE TESTS: calling decision function:\")\n29\n(terpri)\n30\n(dolist (pos (cadr test)) ;; positive test examples\n31\n(let ((prediction (funcall decision-function pos)))\n32\n(print prediction)\n33\n(if prediction (incf correct-positives) (incf wrong-positives))))\n34\n(format t \"Number of correct negatives ~a~%\" correct-negatives)\n35\n(format t \"Number of wrong negatives ~a~%\" wrong-negatives)\n36\n(format t \"Number of correct positives ~a~%\" correct-positives)\n37\n(format t \"Number of wrong positives ~a~%\" wrong-positives)))\n38\n39\n40\n(defun cancer-data-svm-example-read-data ()\n41\n42\n(defun split-positive-negative-cases (data)\n43\n(let ((negative-cases '())\n\nUsing The CL Machine-Learning Library\n140\n44\n(positive-cases '()))\n45\n(dolist (d data)\n46\n;;(print (list \"*\nd=\" d))\n47\n(if (equal (symbol-name (first (last d))) \"benign\")\n48\n(setf negative-cases\n49\n(cons (reverse (cdr (reverse d))) negative-cases))\n50\n(setf positive-cases\n51\n(cons (reverse (cdr (reverse d))) positive-cases))))\n52\n(list negative-cases positive-cases)))\n53\n54\n(let* ((train1\n55\n(clml.hjs.read-data:read-data-from-file\n56\n\"./machine_learning_data/labeled_cancer_training_data.csv\"\n57\n:type :csv\n58\n:csv-type-spec (append (make-list 9 :initial-element 'double-float)\n59\n'(symbol))))\n60\n(train-as-list\n61\n(split-positive-negative-cases\n62\n(coerce\n63\n(map 'list\n64\n#'(lambda (x) (coerce x 'list))\n65\n(coerce (clml.hjs.read-data:dataset-points train1) 'list))\n66\n'list)))\n67\n(test1\n68\n(clml.hjs.read-data:read-data-from-file\n69\n\"./machine_learning_data/labeled_cancer_test_data.csv\"\n70\n:type :csv\n71\n:csv-type-spec (append (make-list 9 :initial-element 'double-float)\n72\n'(symbol))))\n73\n(test-as-list\n74\n(split-positive-negative-cases\n75\n(coerce\n76\n(map 'list\n77\n#'(lambda (x) (coerce x 'list))\n78\n(coerce (clml.hjs.read-data:dataset-points test1) 'list))\n79\n'list))))\n80\n81\n;; we will use a gaussian kernel for numeric data.\n82\n;; note: for text classification, use a clml.svm.mu:+linear-kernel+\n83\n(svm-classifier-test\n84\n(clml.svm.mu:gaussian-kernel 2.0d0)\n85\ntrain-as-list test-as-list)))\n86\n\nUsing The CL Machine-Learning Library\n141\n87\n(cancer-data-svm-example-read-data)\nThe sample code prints the prediction values for the test data which I will not show here. Here are\nthe last four lines of output showing the cumulative statistics for the test data:\n1\nNumber of correct negatives 219\n2\nNumber of wrong negatives 4\n3\nNumber of correct positives 116\n4\nNumber of wrong positives 6\nCLML Wrap Up\nThe CLML machine learning library is under fairly active development and I showed you enough to\nget started: understanding the data APIs and examples for KNN clustering and SVM classification.\nA good alternative to CLML is MGL⁶⁸ that supports backpropagation neural networks, boltzmann\nmachines, and gaussian processes.\nIn the next two chapters we continue with the topic of machine learning with backpropagation andf\nHopfield neural networks.\n⁶⁸https://github.com/melisgl/mgl\n\nBackpropagation Neural Networks\nLet’s start with an overview of how these networks work and then fill in more detail later.\nBackpropagation networks are trained by applying training inputs to the network input layer,\npropagate values through the network to the output neurons, compare the errors (or differences)\nbetween these propagated output values and the training data output values. These output errors\nare backpropagated though the network and the magnitude of backpropagated errors are used to\nadjust the weights in the network.\nThe example we look at here uses the plotlib package from an earlier chapter and the source code\nfor the example is the file loving_snippet/backprop_neural_network.lisp.\nWe will use the following diagram to make this process more clear. There are four weights in this\nvery simple network:\n• W¹,¹ is the floating point number representing the connection strength between input_neuron¹\nand output_neuron¹\n• W²,¹ connects input_neuron² to output_neuron¹\n• W¹,² connects input_neuron¹ to output_neuron²\n• W²,² connects input_neuron² to output_neuron²\nUnderstanding how connection weights connect neurons in adjacent layers\n\nBackpropagation Neural Networks\n143\nBefore any training the weight values are all small random numbers.\nConsider a training data element where the input neurons have values [0.1, 0.9] and the desired\noutput neuron values are [0.9 and 0.1], that is flipping the input values. If the propagated output\nvalues for the current weights are [0.85, 0.5] then the value of the first output neuron has a small\nerror abs(0.85 - 0.9) which is 0.05. However the propagated error of the second output neuron is high:\nabs(0.5 - 0.1) which is 0.4. Informally we see that the weights feeding input output neuron 1 (W¹,¹\nand W²,¹) don’t need to be changed much but the neuron that feeding input neuron 2 (W¹,² and W²,²)\nneeds modification (the value of W²,² is too large).\nOf course, we would never try to manually train a network like this but it is important to have at least\nan informal understanding of how weights connect the flow of value (we will call this activation\nvalue later) between neurons.\nIn this neural network see in the first figure we have four weights connecting the input and output\nneurons. Think of these four weights forming a four-dimensional space where the range in each\ndimension is constrained to small positive and negative floating point values. At any point in this\n“weight space”, the numeric values of the weights defines a model that maps the inputs to the outputs.\nThe error seen at the output neurons is accumulated for each training example (applied to the input\nneurons). The training process is finding a point in this four-dimensional space that has low errors\nsummed across the training data. We will use gradient descent to start with a random point in the\nfour-dimensional space (i.e., an initial random set of weights) and move the point towards a local\nminimum that represents the weights in a model that is (hopefully) “good enough” at representing\nthe training data.\nThis process is simple enough but there are a few practical considerations:\n• Sometimes the accumulated error at a local minimum is too large even after many training\ncycles and it is best to just restart the training process with new random weights.\n• If we don’t have enough training data then the network may have enough memory capacity\nto memorize the training examples. This is not what we want: we want a model with just\nenough memory capacity (as represented by the number of weights) to form a generalized\npredictive model, but not so specific that it just memorizes the training examples. The solution\nis to start with small networks (few hidden neurons) and increase the number of neurons until\nthe training data can be learned. In general, having a lot of training data is good and it is also\ngood to use as small a network as possible.\nIn practice using backpropagation networks is an iterative process of experimenting with the size of\na network.\nIn the example program (in the file backprop_neural_network.lisp) we use the plotting library\ndeveloped earlier to visualize neuron activation and connecting weight values while the network\ntrains.\nThe following three screen shots from running the function test3 defined at the bottom of the\nfile backprop_neural_network.lisp illustrate the process of starting with random weights, getting\n\nBackpropagation Neural Networks\n144\nrandom outputs during initial training, and as delta weights are used to adjust the weights in a\nnetwork, then the training examples are learned:\nAt the start of the training run with random weights and large delta weights\nIn the last figure the initial weights are random so we get random mid-range values at the output\nneurons.\nThe trained weights start to produce non-random output\nAs we start to train the network, adjusting the weights, we start to see variation in the output neurons\nas a function of what the inputs are.\n\nBackpropagation Neural Networks\n145\nAfter training many cycles the training examples are learned, with only small output errors\nIn the last figure the network is trained sufficiently well to map inputs [0, 0, 0, 1] to output values\nthat are approximately [0.8, 0.2, 0.2, 0.3] which is close to the expected value [1, 0, 0, 0].\nThe example source file backprop_neural_network.lisp is long so we will only look at the more\ninteresting parts here. Specifically we will not look at the code to plot neural networks using plotlib.\nThe activation values of individual neurons are limited to the range [0, 1] by first calculating their\nvalues based on the sum activation values of neurons in the previous layer times the values of the\nconnecting weights and then using the Sigmoid function to map the sums to the desired range. The\nSigmoid function and the derivative of the Sigmoid function (dSigmoid) look like:\nSigmoid and Derivative of the Sigmid Functions\nHere are the definitions of these functions:\n\nBackpropagation Neural Networks\n146\n(defun Sigmoid (x)\n(/ 1.0 (+ 1.0 (exp (- x)))))\n(defun dSigmoid (x)\n(let ((temp (Sigmoid x)))\n(* temp (- 1.0 temp)))\nThe function NewDeltaNetwork creates a new neual network object. This code allocates storage\nfor input, hidden, output layers (I sometimes refer to neuron layers as “slabs”), and the connection\nweights. Connection weights are initialized to small random values.\n1\n; (NewDeltaNetwork sizeList)\n2\n;\nArgs:\nsizeList = list of sizes of slabs. This also defines\n3\n;\nthe number of slabs in the network.\n4\n;\n(e.g.,\n'(10 5 4) ==> a 3-slab network with 10\n5\n;\ninput neurons, 5 hidden neurons, and 4 output\n6\n;\nneurons).\n7\n;\n8\n;\nReturned value = a list describing the network:\n9\n;\n(nLayers sizeList\n10\n;\n(activation-array[1] .. activation-array[nLayers])\n11\n;\n(weight-array[2] .. weight-array[nLayers])\n12\n;\n(sum-of-products[2] .. sum-of-products[nLayers[nLayers])\n13\n;\n(back-prop-error[2] .. back-prop-error[nLayers]))\n14\n;\n(old-delta-weights[2] .. for momentum term\n15\n16\n:initial-element 0.0))\n17\n(reverse old-dw-list)))\n18\n19\n;;\n20\n;\nInitialize values for all activations:\n21\n;;\n22\n(mapc\n23\n(lambda (x)\n24\n(let ((num (array-dimension x 0)))\n25\n(dotimes (n num)\n26\n(setf (aref x n) (frandom 0.01 0.1)))))\n27\na-list)\n28\n29\n;;\n30\n;\nInitialize values for all weights:\n31\n;;\n32\n(mapc\n\nBackpropagation Neural Networks\n147\n33\n(lambda (x)\n34\n(let ((numI (array-dimension x 0))\n35\n(numJ (array-dimension x 1)))\n36\n(dotimes (j numJ)\n37\n(dotimes (i numI)\n38\n(setf (aref x i j) (frandom -0.5 0.5))))))\n39\nw-list)\n40\n(list numLayers sizeList a-list s-list w-list dw-list\n41\nd-list old-dw-list alpha beta)))\nIn the following listing the function DeltaLearn processes one pass through all of the training data.\nFunction DeltaLearn is called repeatedly until the return value is below a desired error threshold.\nThe main loop over each training example is implemented in lines 69-187. Inside this outer loop there\nare two phases of training for each training example: a forward pass propagating activation from\nthe input neurons to the output neurons via any hidden layers (lines 87-143) and then the weight\ncorrecting backpropagation of output errors while making small adjustments to weights (lines 148-\n187):\n1\n;;\n2\n;\nUtility function for training a delta rule neural network.\n3\n;\nThe first argument is the name of an output PNG plot file\n4\n;\nand a nil value turns off plotting the network during training.\n5\n;\nThe second argument is a network definition (as returned from\n6\n;\nNewDeltaNetwork), the third argument is a list of training\n7\n;\ndata cases (see the example test functions at the end of this\n8\n;\nfile for examples.\n9\n;;\n10\n11\n(defun DeltaLearn (plot-output-file-name\n12\nnetList trainList)\n13\n(let ((nLayers (car netList))\n14\n(sizeList (cadr netList))\n15\n(activationList (caddr netList))\n16\n(sumOfProductsList (car (cdddr netList)))\n17\n(weightList (cadr (cdddr netList)))\n18\n(deltaWeightList (caddr (cdddr netList)))\n19\n(deltaList (cadddr (cdddr netList)))\n20\n(oldDeltaWeightList (cadddr (cdddr (cdr netList))))\n21\n(alpha (cadddr (cdddr (cddr netList))))\n22\n(beta (cadddr (cdddr (cdddr netList))))\n23\n(inputs nil)\n24\n(targetOutputs nil)\n25\n(iDimension nil)\n\nBackpropagation Neural Networks\n148\n26\n(jDimension nil)\n27\n(iActivationVector nil)\n28\n(jActivationVector nil)\n29\n(n nil)\n30\n(weightArray nil)\n31\n(sumOfProductsArray nil)\n32\n(iDeltaVector nil)\n33\n(jDeltaVector nil)\n34\n(deltaWeightArray nil)\n35\n(oldDeltaWeightArray nil)\n36\n(sum nil)\n37\n(iSumOfProductsArray nil)\n38\n(error nil)\n39\n(outputError 0)\n40\n(delta nil)\n41\n(eida nil)\n42\n(inputNoise 0))\n43\n44\n;;\n45\n; Zero out deltas:\n46\n;;\n47\n(dotimes (n (- nLayers 1))\n48\n(let* ((dw (nth n deltaList))\n49\n(len1 (array-dimension dw 0)))\n50\n(dotimes (i len1)\n51\n(setf (aref dw i) 0.0))))\n52\n53\n;;\n54\n; Zero out delta weights:\n55\n;;\n56\n(dotimes (n (- nLayers 1))\n57\n(let* ((dw (nth n deltaWeightList))\n58\n(len1 (array-dimension dw 0))\n59\n(len2 (array-dimension dw 1)))\n60\n(dotimes (i len1)\n61\n(dotimes (j len2)\n62\n(setf (aref dw i j) 0.0)))))\n63\n64\n(setq inputNoise *delta-default-input-noise-value*)\n65\n66\n;;\n67\n;\nMain loop on training examples:\n68\n;;\n\nBackpropagation Neural Networks\n149\n69\n(dolist (tl trainList)\n70\n71\n(setq inputs (car tl))\n72\n(setq targetOutputs (cadr tl))\n73\n74\n(if *delta-rule-debug-flag*\n75\n(print (list \"Current targets:\" targetOutputs)))\n76\n77\n(setq iDimension (car sizeList)) ; get the size of the input slab\n78\n(setq iActivationVector (car activationList)) ; input activations\n79\n(dotimes (i iDimension) ; copy training inputs to input slab\n80\n(setf\n81\n(aref iActivationVector i)\n82\n(+ (nth i inputs) (frandom (- inputNoise) inputNoise))))\n83\n;;\n84\n; Propagate activation through all of the slabs:\n85\n;;\n86\n(dotimes (n-1 (- nLayers 1))\n; update layer i to layer flowing to layer j\n87\n(setq n (+ n-1 1))\n88\n(setq jDimension (nth n sizeList)) ; get the size of the j'th layer\n89\n(setq jActivationVector (nth n activationList)) ; activation\nfor slab j\n90\n(setq weightArray (nth n-1 weightList))\n91\n(setq sumOfProductsArray (nth n-1 sumOfProductsList))\n92\n(dotimes (j jDimension) ; process each neuron in slab j\n93\n(setq sum 0.0) ; init sum of products to zero\n94\n(dotimes (i iDimension) ; activation from neurons in previous slab\n95\n(setq\n96\nsum\n97\n(+ sum (* (aref weightArray i j) (aref iActivationVector i)))))\n98\n(setf (aref sumOfProductsArray j) sum) ; save sum of products\n99\n(setf (aref jActivationVector j) (Sigmoid sum)))\n100\n(setq iDimension jDimension)\n; reset index for next slab pair\n101\n(setq iActivationVector jActivationVector))\n102\n;;\n103\n; Activation is\nspread through the network and sum of products\n104\n; calculated. Now modify the weights in the network using back\n105\n; error propagation. Start by calculating the error signal for\n106\n; each neuron in the output layer:\n107\n;;\n108\n(setq jDimension (nth (- nLayers 1) sizeList)) ; size of last layer\n109\n(setq jActivationVector (nth (- nLayers 1) activationList))\n110\n(setq jDeltaVector (nth (- nLayers 2) deltaList))\n111\n(setq sumOfProductsArray (nth (- nLayers 2) sumOfProductsList))\n\nBackpropagation Neural Networks\n150\n112\n(setq outputError 0)\n113\n(dotimes (j jDimension)\n114\n(setq delta (- (nth j targetOutputs) (aref jActivationVector j)))\n115\n(setq outputError (+ outputError (abs delta)))\n116\n(setf\n117\n(aref jDeltaVector j)\n118\n(+\n119\n(aref jDeltaVector j)\n120\n(* delta (dSigmoid (aref sumOfProductsArray j))))))\n121\n;;\n122\n; Now calculate the backpropagated error signal for all hidden slabs:\n123\n;;\n124\n(dotimes (nn (- nLayers 2))\n125\n(setq n (- nLayers 3 nn))\n126\n(setq iDimension (nth (+ n 1) sizeList))\n127\n(setq iSumOfProductsArray (nth n sumOfProductsList))\n128\n(setq iDeltaVector (nth n deltaList))\n129\n(dotimes (i iDimension)\n130\n(setf (aref iDeltaVector i) 0.0))\n131\n(setq weightArray (nth (+ n 1) weightList))\n132\n(dotimes (i iDimension)\n133\n(setq error 0.0)\n134\n(dotimes (j jDimension)\n135\n(setq error\n136\n(+ error (* (aref jDeltaVector j) (aref weightArray i j)))))\n137\n(setf\n138\n(aref iDeltaVector i)\n139\n(+\n140\n(aref iDeltaVector i)\n141\n(* error (dSigmoid (aref iSumOfProductsArray i))))))\n142\n(setq jDimension iDimension)\n143\n(setq jDeltaVector iDeltaVector))\n144\n145\n;;\n146\n; Update all delta weights in the network:\n147\n;;\n148\n(setq iDimension (car sizeList))\n149\n(dotimes (n (- nLayers 1))\n150\n(setq iActivationVector (nth n activationList))\n151\n(setq jDimension (nth (+ n 1) sizeList))\n152\n(setq jDeltaVector (nth n deltaList))\n153\n(setq deltaWeightArray (nth n deltaWeightList))\n154\n(setq weightArray (nth n weightList))\n\nBackpropagation Neural Networks\n151\n155\n(setq eida (nth n eidaList))\n156\n157\n(dotimes (j jDimension)\n158\n(dotimes (i iDimension)\n159\n(setq delta (* eida (aref jDeltaVector j) (aref iActivationVector i)))\n160\n(setf\n161\n(aref DeltaWeightArray i j)\n162\n(+ (aref DeltaWeightArray i j) delta)))) ; delta weight changes\n163\n164\n(setq iDimension jDimension))\n165\n166\n;;\n167\n; Update all weights in the network:\n168\n;;\n169\n(setq iDimension (car sizeList))\n170\n(dotimes (n (- nLayers 1))\n171\n(setq iActivationVector (nth n activationList))\n172\n(setq jDimension (nth (+ n 1) sizeList))\n173\n(setq jDeltaVector (nth n deltaList))\n174\n(setq deltaWeightArray (nth n deltaWeightList))\n175\n(setq oldDeltaWeightArray (nth n oldDeltaWeightList))\n176\n(setq weightArray (nth n weightList))\n177\n(dotimes (j jDimension)\n178\n(dotimes (i iDimension)\n179\n(setf\n180\n(aref weightArray i j)\n181\n(+ (aref weightArray i j)\n182\n(* alpha (aref deltaWeightArray i j))\n183\n(* beta\n(aref oldDeltaWeightArray i j))))\n184\n(setf (aref oldDeltaWeightArray i j) ; save current delta weights\n185\n(aref deltaWeightArray i j)))) ; ...for next momentum term.\n186\n(setq iDimension jDimension))\n187\n188\n(if plot-output-file-name\n189\n(DeltaPlot netList plot-output-file-name)))\n190\n191\n(/ outputError jDimension)))\nThe function DeltaRecall in the next listing can be used with a trained network to calculate outputs\nfor new input values:\n\nBackpropagation Neural Networks\n152\n1\n;;\n2\n;\nUtility for using a trained neural network in the recall mode.\n3\n;\nThe first argument to this function is a network definition (as\n4\n;\nreturned from NewDeltaNetwork) and the second argument is a list\n5\n;\nof input neuron activation values to drive through the network.\n6\n;\nThe output is a list of the calculated activation energy for\n7\n;\neach output neuron.\n8\n;;\n9\n(defun DeltaRecall (netList inputs)\n10\n(let ((nLayers (car netList))\n11\n(sizeList (cadr netList))\n12\n(activationList (caddr netList))\n13\n(weightList (cadr (cdddr netList)))\n14\n(iDimension nil)\n15\n(jDimension nil)\n16\n(iActivationVector nil)\n17\n(jActivationVector nil)\n18\n(n nil)\n19\n(weightArray nil)\n20\n(returnList nil)\n21\n(sum nil))\n22\n(setq iDimension (car sizeList)) ; get the size of the input slab\n23\n(setq iActivationVector (car activationList)) ; get input activations\n24\n(dotimes (i iDimension) ; copy training inputs to input slab\n25\n(setf (aref iActivationVector i) (nth i inputs)))\n26\n(dotimes (n-1 (- nLayers 1))\n; update layer j to layer i\n27\n(setq n (+ n-1 1))\n28\n(setq jDimension (nth n sizeList)) ; get the size of the j'th layer\n29\n(setq jActivationVector (nth n activationList)) ; activation for slab j\n30\n(setq weightArray (nth n-1 weightList))\n31\n(dotimes (j jDimension) ; process each neuron in slab j\n32\n(setq sum 0.0) ; init sum of products to zero\n33\n(dotimes (i iDimension) ; get activation from each neuron in last slab\n34\n(setq\n35\nsum\n36\n(+ sum (* (aref weightArray i j) (aref iActivationVector i)))))\n37\n(if *delta-rule-debug-flag*\n38\n(print (list \"sum=\" sum)))\n39\n(setf (aref jActivationVector j) (Sigmoid sum)))\n40\n(setq iDimension jDimension) ; get ready for next slab pair\n41\n(setq iActivationVector jActivationVector))\n42\n(dotimes (j jDimension)\n43\n(setq returnList (append returnList (list (aref jActivationVector j)))))\n\nBackpropagation Neural Networks\n153\n44\nreturnList))\nWe saw three output plots earlier that were produced during a training run using the following code:\n1\n(defun test3 (&optional (restart 'yes) &aux RMSerror) ; three layer network\n2\n(if\n3\n(equal restart 'yes)\n4\n(setq temp (newdeltanetwork '(5 4 5))))\n5\n(dotimes (ii 3000)\n6\n(let ((file-name\n7\n(if (equal (mod ii 400) 0)\n8\n(concatenate 'string \"output_plot_\" (format nil \"~12,'0d\" ii) \".png\")\n9\nnil)))\n10\n(setq\n11\nRMSerror\n12\n(deltalearn\n13\nfile-name temp\n14\n'(((1 0 0 0 0) (0 1 0 0 0))\n15\n((0 1 0 0 0) (0 0 1 0 0))\n16\n((0 0 1 0 0) (0 0 0 1 0))\n17\n((0 0 0 1 0) (0 0 0 0 1))\n18\n((0 0 0 0 1) (1 0 0 0 0)))))\n19\n(if (equal (mod ii 50) 0) ;; print error out every 50 cycles\n20\n(progn\n21\n(princ \"....training cycle \\#\")\n22\n(princ ii)\n23\n(princ \" RMS error = \")\n24\n(princ RMSerror)\n25\n(terpri))))))\nHere the function test3 defines training data for a very small test network for a moderately difficult\nfunction to learn: to rotate the values in the input neurons to the right, wrapping around to the first\nneuron. The start of the main loop in line calls the training function 3000 times, creating a plot of\nthe network every 400 times through the main loop.\nBackpropagation networks have been used sucessfully in production for about 25 years. In the next\nchapter we will look at a less practical type of network, Hopfield networks, that are still interesting\nbecause the in some sense Hopfield networks model how our brains work. In the final chapter we\nwill look at deep learning neural networks.\n\nHopfield Neural Networks\nA Hopfield network⁶⁹ (named after John Hopfield) is a recurrent network since the flow of activation\nthrough the network has loops. These networks are trained by applying input patterns and letting\nthe network settle in a state that stores the input patterns.\nThe example code is in the file src/loving_snippets/Hopfield_neural_network.lisp.\nThe example we look at recognizes patterns that are similar to the patterns seen in training examples\nand maps input patterns to a similar training input pattern. The following figure shows output from\nthe example program showing an original training pattern, a similar pattern with one cell turned\non and other off, and the reconstructed pattern:\nTo be clear, we have taken one of the original input patterns the network has learned, slightly altered\nit, and applied it as input to the network. After cycling the network, the slightly scrambled input\npattern we just applied will be used as an associative memory key, look up the original pattern,\nand rewrite to input values with the original learned pattern. These Hopfield networks are very\ndifferent than backpropagation networks: neuron activation are forced to values of -1 or +1 and not\nbe differentiable and there are no separate output neurons.\nThe next example has the values of three cells modified from the original and the original pattern is\nstill reconstructed correctly:\n⁶⁹https://en.wikipedia.org/wiki/Hopfield_network\n\nHopfield Neural Networks\n155\nThis last example has four of the original cells modified:\nThe following example program shows a type of content-addressable memory. After a Hopfield\nnetwork learns a set of input patterns then it can reconstruct the original paterns when shown\nsimilar patterns. This reconstruction is not always perfecrt.\nThe following function Hopfield-Init (in file Hopfield_neural_network.lisp) is passed a list of lists of\ntraining examples that will be remembered in the network. This function returns a list containing the\ndata defining a Hopfield neural network. All data for the network is encapsulated in the list returned\nby this function, so multiple Hopfield neural networks can be used in an application program.\nIn lines 9-12 we allocate global arrays for data storage and in lines 14-18 the training data is copied.\nThe inner function adjustInput on lines 20-29 adjusts data values to values of -1.0 or +1.0. In lines\n31-33 we are initializing all of the weights in the Hopfield network to zero.\nThe last nested loop, on lines 35-52, calculates the autocorrelation weight matrix from the input test\npatterns.\nOn lines 54-56, the function returns a representation of the Hopfield network that will be used later in\nthe function HopfieldNetRecall to find the most similar “remembered” pattern given a new (fresh)\ninput pattern.\n\nHopfield Neural Networks\n156\n1\n(defun Hopfield-Init (training-data\n2\n&aux temp *num-inputs* *num-training-examples*\n3\n*training-list* *inputCells* *tempStorage*\n4\n*HopfieldWeights*)\n5\n6\n(setq *num-inputs* (length (car training-data)))\n7\n(setq *num-training-examples* (length training-data))\n8\n9\n(setq *training-list* (make-array (list *num-training-examples* *num-inputs*)))\n10\n(setq *inputCells* (make-array (list *num-inputs*)))\n11\n(setq *tempStorage* (make-array (list *num-inputs*)))\n12\n(setq *HopfieldWeights* (make-array (list *num-inputs* *num-inputs*)))\n13\n14\n(dotimes (j *num-training-examples*) ;; copy training data\n15\n(dotimes (i *num-inputs*)\n16\n(setf\n17\n(aref *training-list* j i)\n18\n(nth i (nth j training-data)))))\n19\n20\n(defun adjustInput (value)\n;; this function is lexically scoped\n21\n(if (< value 0.1)\n22\n-1.0\n23\n+1.0))\n24\n25\n(dotimes (i *num-inputs*) ;; adjust training data\n26\n(dotimes (n *num-training-examples*)\n27\n(setf\n28\n(aref *training-list* n i)\n29\n(adjustInput (aref *training-list* n i)))))\n30\n31\n(dotimes (i *num-inputs*) ;; zero weights\n32\n(dotimes (j *num-inputs*)\n33\n(setf (aref *HopfieldWeights* i j) 0)))\n34\n35\n(dotimes (j-1 (- *num-inputs* 1)) ;; autocorrelation weight matrix\n36\n(let ((j (+ j-1 1)))\n37\n(dotimes (i j)\n38\n(dotimes (s *num-training-examples*)\n39\n(setq temp\n40\n(truncate\n41\n(+\n42\n(*\n;; 2 if's truncate values to -1 or 1:\n43\n(adjustInput (aref *training-list* s i))\n\nHopfield Neural Networks\n157\n44\n(adjustInput (aref *training-list* s j)))\n45\n(aref *HopfieldWeights* i j))))\n46\n(setf (aref *HopfieldWeights* i j) temp)\n47\n(setf (aref *HopfieldWeights* j i) temp)))))\n48\n(dotimes (i *num-inputs*)\n49\n(setf (aref *tempStorage* i) 0)\n50\n(dotimes (j i)\n51\n(setf (aref *tempStorage* i)\n52\n(+ (aref *tempStorage* i) (aref *HopfieldWeights* i j)))))\n53\n54\n(list ;; return the value of the Hopfield network data object\n55\n*num-inputs* *num-training-examples* *training-list*\n56\n*inputCells* *tempStorage* *HopfieldWeights*))\nThe following function HopfieldNetRecall iterates the network to let it settle in a stable pattern\nwhich we hope will be the original training pattern most closely resembling the noisy test pattern.\nThe inner (lexically scoped) function deltaEnergy defined on lines 9-12 calculates a change in energy\nfrom old input values and the autocorrelation weight matrix. The main code uses the inner functions\nto iterate over the input cells, possibly modifying the cell at index i delta energy is greater than zero.\nRemember that the lexically scoped inner functions have access to the variables for the number of\ninputs, the number of training examples, the list of training examples, the input cell values, tempoary\nstorage, and the Hopfield network weights.\n1\n(defun HopfieldNetRecall (aHopfieldNetwork numberOfIterations)\n2\n(let ((*num-inputs* (nth 0 aHopfieldNetwork))\n3\n(*num-training-examples*\n(nth 1 aHopfieldNetwork))\n4\n(*training-list* (nth 2 aHopfieldNetwork))\n5\n(*inputCells* (nth 3 aHopfieldNetwork))\n6\n(*tempStorage* (nth 4 aHopfieldNetwork))\n7\n(*HopfieldWeights* (nth 5 aHopfieldNetwork)))\n8\n9\n(defun deltaEnergy (row-index y &aux (temp 0.0))\n;; lexically scoped\n10\n(dotimes (j *num-inputs*)\n11\n(setq temp (+ temp (* (aref *HopfieldWeights* row-index j) (aref y j)))))\n12\n(- (* 2.0 temp) (aref *tempStorage* row-index)))\n13\n14\n(dotimes (ii numberOfIterations) ;; main code\n15\n(dotimes (i *num-inputs*)\n16\n(setf (aref *inputCells* i)\n17\n(if (> (deltaEnergy i *inputCells*) 0)\n18\n1\n19\n0))))))\n\nHopfield Neural Networks\n158\nFunction test in the next listing uses three different patterns for each test. Note that only the last\npattern gets plotted to the output graphics PNG file for the purpose of producing figures for this\nchapter. If you want to produce plots of other patterns, edit just the third pattern defined on line\nAAAAA. The following plotting functions are inner lexically scoped so they have access to the data\ndefined in the enclosing let expression in lines 16-21:\n• plotExemplar - plots a vector of data\n• plot-original-inputCells - plots the original input cells from training data\n• plot-inputCells - plots the modified input cells (a few cells randomly flipped in value)\n• modifyInput - scrambles training inputs\n1\n(defun test (&aux aHopfieldNetwork)\n2\n(let ((tdata '(\n;; sample sine wave data with different periods:\n3\n(1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 1 1 0 0 0)\n4\n(0 1 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1 0 0 0 1 1 0 0 1 0)\n5\n(0 0 0 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 1 1 0 1 1)))\n6\n(width 300)\n7\n(height 180))\n8\n(vecto::with-canvas (:width width :height height)\n9\n(plotlib:plot-string-bold 10 (- height 14) \"Hopfield pattern classifier\")\n10\n11\n;; Set up network:\n12\n(print tdata)\n13\n(setq aHopfieldNetwork (Hopfield-Init tdata))\n14\n15\n;; lexically scoped variables are accesible by inner functions:\n16\n(let ((*num-inputs* (nth 0 aHopfieldNetwork))\n17\n(*num-training-examples*\n(nth 1 aHopfieldNetwork))\n18\n(*training-list* (nth 2 aHopfieldNetwork))\n19\n(*inputCells* (nth 3 aHopfieldNetwork))\n20\n(*tempStorage* (nth 4 aHopfieldNetwork))\n21\n(*HopfieldWeights* (nth 5 aHopfieldNetwork)))\n22\n23\n(defun plotExemplar (row &aux (dmin 0.0) (dmax 1.0) (x 20) (y 40))\n24\n(let ((YSize (array-dimension *training-list* 1)))\n25\n(plotlib:plot-string (+ x 20) (- height (- y 10))\n26\n\"Original Training Exemplar\")\n27\n(dotimes (j Ysize)\n28\n(plotlib:plot-fill-rect\n29\n(+ x (* j plot-size+1)) (- height y) plot-size plot-size\n30\n(truncate (*\n31\n(/\n(- (aref *training-list* row j) dmin)\n\nHopfield Neural Networks\n159\n32\n(- dmax dmin))\n33\n5)))\n34\n(plotlib:plot-frame-rect (+ x (* j plot-size+1))\n35\n(- height y) plot-size plot-size))))\n36\n37\n(defun plot-original-inputCells (&aux (dmin 0.0) (dmax 1.0) (x 20) (y 80))\n38\n(let ((Xsize (array-dimension *inputCells* 0)))\n39\n(plotlib:plot-string (+ x 20) (- height (- y 10)) \"Scrambled Inputs\")\n40\n(dotimes (j Xsize)\n41\n(plotlib:plot-fill-rect\n42\n(+ x (* j plot-size+1)) (- height y) plot-size plot-size\n43\n(truncate (*\n44\n(/\n(- (aref *inputCells* j) dmin) (- dmax dmin))\n45\n5)))\n46\n(plotlib:plot-frame-rect (+ x (* j plot-size+1))\n47\n(- height y) plot-size plot-size))))\n48\n49\n(defun plot-inputCells (&aux (dmin 0.0) (dmax 1.0) (x 20) (y 120))\n50\n(let ((Xsize (array-dimension *inputCells* 0)))\n51\n(plotlib:plot-string (+ x 20) (- height (- y 10))\n52\n\"Reconstructed Inputs\")\n53\n(dotimes (j Xsize)\n54\n(plotlib:plot-fill-rect\n55\n(+ x (* j plot-size+1)) (- height y) plot-size plot-size\n56\n(truncate (* (/\n57\n(- (aref *inputCells* j) dmin)\n58\n(- dmax dmin))\n59\n5)))\n60\n(plotlib:plot-frame-rect\n61\n(+ x (* j plot-size+1)) (- height y) plot-size plot-size))))\n62\n63\n(defun modifyInput (arrSize arr)\n;; modify input array for testing\n64\n(dotimes (i arrSize)\n65\n(if (< (random 50) 5)\n66\n(if (> (aref arr i) 0)\n67\n(setf (aref arr i) -1)\n68\n(setf (aref arr i) 1)))))\n69\n70\n;; Test network on training data that is randomly modified:\n71\n72\n(dotimes (iter 10) ;; cycle 10 times and make 10 plots\n73\n(dotimes (s *num-training-examples*)\n74\n(dotimes (i *num-inputs*)\n\nHopfield Neural Networks\n160\n75\n(setf (aref *inputCells* i) (aref *training-list* s i)))\n76\n(plotExemplar s)\n77\n(modifyInput *num-inputs* *inputCells*)\n78\n(plot-original-inputCells)\n79\n(dotimes (call-net 5) ;; iterate Hopfield net 5 times\n80\n(HopfieldNetRecall aHopfieldNetwork 1)\n;; calling with 1 iteration\n81\n(plot-inputCells)))\n82\n83\n(vecto::save-png\n84\n(concatenate\n85\n'string\n86\n\"output_plot_hopfield_nn_\" (format nil \"~5,'0d\" iter) \".png\")))))))\nThe plotting functions in lines 23-62 use the plotlib library to make the plots you saw earlier. The\nfunction modifyInput in lines 64-69 randomly flips the values of the input cells, taking an original\npattern and slightly modifying it.\nHopfield neural networks, at least to some extent, seem to model some aspects of human brains in\nthe sense that they can function as content-addressable (also called associative) memories. Ideally\na partial input pattern from a remembered input can reconstruct the complete original pattern.\nAnother interesting feature of Hopfield networks is that these memories really are stored in a\ndistributed fashion: some of the weights can be randomly altered and patterns are still remembered,\nbut with more recall errors.\n\nUsing Python Deep Learning Models\nIn Common Lisp With a Web Services\nInterface\nIn older editions of this book I had an example of using the Java DeepLearning4J deep learning\nlibrary using Armed Bear Common Lisp, implemented in Java. I no longer use hybrid Java and\nCommon Lisp applications in my own work and I decided to remove this example and replace it\nwith two projects that use simple Python web services that act as wrappers for state of the art deep\nlearning models with Common Lisp clients in the subdirectories:\n• src/spacy_web_client: use the spaCy deep learning models for general NLP. I sometimes use\nmy own pure Common Lisp NLP libraries we saw in earlier chapters and sometimes I use a\nCommon Lisp client calling deep learning libraries like spaCy and TensorFlow.\n• src/coref_web_client: coreference or anaphora resolution is the act of replacing pronouns in\ntext with the original nouns that they refer to. This has traditionally been a very difficult and\nonly partially solved problem until recent advances in deep learning models like BERT.\nNote: in the next chapter we will cover similar functionality but we will use the py4cl library to\nmore directly use Python and libraries like spaCy by starting another Python process and using\nstreams for communication.\nSetting up the Python Web Services Used in this\nChapter\nYou will need python and pip installed on your system. The source e code for the Python web services\nis found in the directory loving-common-lisp/python.\nInstalling the spaCY NLP Services\nI assume that you have some familiarity with using Python. If not, you will still be able to follow these\ndirections assuming that you have the utilities pip, and python installed. I recommend installing\nPython and Pip using Anaconda⁷⁰.\n⁷⁰https://anaconda.org/anaconda/conda\n\nUsing Python Deep Learning Models In Common Lisp With a Web Services Interface\n162\nThe server code is in the subdirectory python/python_spacy_nlp_server where you will work\nwhen performing a one time initialization. After the server is installed you can then run it from the\ncommand line from any directory on your laptop.\nI recommend that you use virtual Python environments when using Python applications to separate\nthe dependencies required for each application or development project. Here I assume that you are\nrunning in a Python version 3.6 or higher environment. First you must install the dependencies:\n1\npip install -U spacy\n2\npython -m spacy download en\n3\npip install falcon\nThen change directory to the subdirectory python/python_spacy_nlp_server in the git repo for\nthis book and install the NLP server:\n1\ncd python/python_spacy_nlp_server\n2\npython setup.py install\nOnce you install the server, you can run it from any directory on your laptop or server using:\n1\nspacynlpserver\nI use deep learning models written in Python using TensorFlow or PyTorch and provide Python\nweb services that can be used in applications I write in Haskell or Common Lisp using web client\ninterfaces for the services written in Python. While it is possible to directly embed models in Haskell\nand Common Lisp, I find it much easier and developer friendly to wrap deep learning models I use\na REST services as I have done here. Often deep learning models only require about a gigabyte of\nmemory and using pre-trained models has lightweight CPU resource needs so while I am developing\non my laptop I might have two or three models running and available as wrapped REST services.\nFor production, I configure both the Python services and my Haskell and Common Lisp applications\nto start automatically on system startup.\nThis is not a Python programming book and I will not discuss the simple Python wrapping code but\nif you are also a Python developer you can easily read and understand the code.\nInstalling the Coreference NLP Services\nI recommend that you use virtual Python environments when using Python applications to separate\nthe dependencies required for each application or development project. Here I assume that you are\nrunning in a Python version 3.6 environment. First you should install the dependencies:\n\nUsing Python Deep Learning Models In Common Lisp With a Web Services Interface\n163\n1\npip install spacy==2.1.0\n2\npip install neuralcoref\n3\npip install falcon\nAs I write this chapter the neuralcoref model and library require a slightly older version of SpaCy\n(the current latest version is 2.1.4).\nThen change directory to the subdirectory python/python_coreference_anaphora_resolution_-\nserver in the git repo for this book and install the coref server:\n1\ncd python_coreference_anaphora_resolution_server\n2\npython setup.py install\nOnce you install the server, you can run it from any directory on your laptop or server using:\n1\ncorefserver\nWhile. as we saw in the last example, it is possible to directly embed models in Haskell and Common\nLisp, I find it much easier and developer friendly to wrap deep learning models I use a REST services\nas I have done here. Often deep learning models only require about a gigabyte of memory and\nusing pre-trained models has lightweight CPU resource needs so while I am developing on my\nlaptop I might have two or three models running and available as wrapped REST services. For\nproduction, I configure both the Python services and my Haskell and Common Lisp applications\nto start automatically on system startup.\nThis is not a Python programming book and I will not discuss the simple Python wrapping code but\nif you are also a Python developer you can easily read and understand the code.\nCommon Lisp Client for the spaCy NLP Web Services\nBefore looking at the code, I will show you typical output from running this example:\n1\n$ sbcl\n2\nThis is SBCL 1.3.16, an implementation of ANSI Common Lisp.\n3\n* (ql:quickload \"spacy-web-client\")\n4\nTo load \"spacy\":\n5\nLoad 1 ASDF system:\n6\nspacy-web-client\n7\n; Loading \"spacy-web-client\"\n8\n.........\n9\n(\"spacy-web-client\")\n10\n* (defvar x\n\nUsing Python Deep Learning Models In Common Lisp With a Web Services Interface\n164\n11\n(spacy-web-client:spacy-client\n12\n\"President Bill Clinton went to Congress. He gave a speech on taxes and Mexico.\"))\n13\n* (spacy-web-client:spacy-data-entities x)\n14\n\"Bill Clinton/PERSON\"\n15\n* (spacy-web-client:spacy-data-tokens x)\n16\n(\"President\" \"Bill\" \"Clinton\" \"went\" \"to\" \"Congress\" \".\" \"He\" \"gave\" \"a\"\n17\n\"speech\" \"on\" \"taxes\" \"and\" \"Mexico\" \".\")\nThe client library is implemented in the file src/spacy_web_client/spacy-web-client.lisp:\n1\n(in-package spacy-web-client)\n2\n3\n(defvar base-url \"http://127.0.0.1:8008?text=\")\n4\n5\n(defstruct spacy-data entities tokens)\n6\n7\n(defun spacy-client (query)\n8\n(let* ((the-bytes\n9\n(drakma:http-request\n10\n(concatenate 'string\n11\nbase-url\n12\n(do-urlencode:urlencode\nquery))\n13\n:content-type \"application/text\"))\n14\n(fetched-data\n15\n(flexi-streams:octets-to-string the-bytes :external-format :utf-8))\n16\n(lists (with-input-from-string (s fetched-data)\n17\n(json:decode-json s))))\n18\n(print lists)\n19\n(make-spacy-data :entities (cadar lists) :tokens (cdadr lists))))\nOn line 3 we define base URL for accessing the spaCy web service, assuming that it is running on\nyour laptop and not a remote server. On line 5 we define a defstruct named spacy-data that has\ntwo fields: a list of entities in the input text and a list of word tokens in the input text.\nThe function spacy-client builds a query string on lines 10-12 that consists of the base-url and the\ninput query text URL encoded. The drakma library, that we used before, is used to make a HTTP\nrequest from the Python spaCy server. Lines 14-15 uses the flexi-streams package to convert raw byte\ndata to UTF8 characters. Lines 16-17 use the json package to parse the UTF8 encoded string, getting\ntwo lists of strings. I left the debug printout expression in line 18 so that you can see the results\nof parsing the JSON data. The function make-spacy-data was generated for us by the defstruct\nstatement on line 5.\n\nUsing Python Deep Learning Models In Common Lisp With a Web Services Interface\n165\nCommon Lisp Client for the Coreference NLP Web\nServices\nLet’s look at some typical output from this example, then we will look at the code:\n1\n$ sbcl\n2\nThis is SBCL 1.3.16, an implementation of ANSI Common Lisp.\n3\nMore information about SBCL is available at <http://www.sbcl.org/>.\n4\n5\nSBCL is free software, provided as is, with absolutely no warranty.\n6\nIt is mostly in the public domain; some portions are provided under\n7\nBSD-style licenses.\nSee the CREDITS and COPYING files in the\n8\ndistribution for more information.\n9\n10\n#P\"/Users/markw/quicklisp/setup.lisp\"\n11\n\"starting up quicklisp\"\n12\n* (ql:quickload \"coref\")\n13\nTo load \"coref\":\n14\nLoad 1 ASDF system:\n15\ncoref\n16\n; Loading \"coref\"\n17\n..................................................\n18\n[package coref]\n19\n(\"coref\")\n20\n* (coref:coref-client \"My sister has a dog Henry. She loves him.\")\n21\n22\n\"My sister has a dog Henry. My sister loves a dog Henry.\"\n23\n* (coref:coref-client \"My sister has a dog Henry. He often runs to her.\")\n24\n25\n\"My sister has a dog Henry. a dog Henry often runs to My sister.\"\nNotice that pronouns in the input text are correctly replaced by the noun phrases that the pronoun\nrefer to.\nThe implementation for the core client is in the file src/coref_web_client/coref.lisp:\n\nUsing Python Deep Learning Models In Common Lisp With a Web Services Interface\n166\n1\n(in-package #:coref)\n2\n3\n;; (ql:quickload :do-urlencode)\n4\n5\n(defvar base-url \"http://127.0.0.1:8000?text=\")\n6\n7\n(defun coref-client (query)\n8\n(let ((the-bytes\n9\n(drakma:http-request\n10\n(concatenate 'string\n11\nbase-url\n12\n(do-urlencode:urlencode\nquery)\n13\n\"&no_detail=1\")\n14\n:content-type \"application/text\")))\n15\n(flexi-streams:octets-to-string the-bytes :external-format :utf-8)))\nThis code is similar to the example in the last section for setting up a call to http-request but is\nsimpler: here the Python coreference web service accepts a string as input and returns a string as\noutput with pronouns replaced by the nouns or noun phrases that they refer to. The example in the\nlast section had to parse returned JSON data, this example does not.\nTrouble Shooting Possible Problems - Skip if this\nExample Works on Your System\nIf you run Common Lisp in an IDE (for example in LispWorks’ IDE or VSCode with a Common Lisp\nplugin) make sure you start the IDE from the command line so your PATH environment variable\nwill be set as it is in our bash or zsh shell.\nMake sure you are starting your Common Lisp program or running a Common Lisp repl with the\nsame Python installation (if you have Quicklisp installed, then you also have the package uiop\ninstalled):\n1\n$ which python\n2\n/Users/markw/bin/anaconda3/bin/python\n3\n$ sbcl\n4\nThis is SBCL 2.0.2, an implementation of ANSI Common Lisp.\n5\n* (uiop:run-program \"which python\" :output :string)\n6\n\"/Users/markw/bin/anaconda3/bin/python\"\n7\nnil\n8\n0\n9\n*\n\nUsing Python Deep Learning Models In Common Lisp With a Web Services Interface\n167\nPython Interop Wrap-up\nMuch of my professional work in the last five years involved deep learning models and currently\nmost available software is written in Python. While there are available libraries for calling Python\ncode from Common Lisp, these libraries tend to not work well for Python code using libraries like\nTensorFlow, spaCy, PyTorch, etc., especially if the Python code is configured to use GPUs via CUDA\nof special hardware like TPUs. I find it simpler to simply wrap functionality implemented in Python\nas a simple web service.\n\nUsing the PY4CL Library to Embed\nPython in Common Lisp\nWe will tackle the same problem as the previous chapter but take a different approach. Now we will\nuse Ben Dudson’s project Py4CL⁷¹ that automatically starts a Python process and communicates\nwith the Python process via a stream interface. The approach we took before is appropriate for large\nscale systems where you might want scale horizontally by having Python processes running on\ndifferent servers than the servers used for the Common Lisp parts of your application. The approach\nwe now take is much more convenient for what I call “laptop development” where the management\nof a Python process and communication is handled for you by the Py4CL library. If you need to\nbuild multi-server distributed systems for scaling reasons then use the examples in the last chapter.\nWhile Py4CL provides a lot of flexibility for passing primitive types between Common Lisp and\nPython (in both directions), I find it easiest to write small Python wrappers that only use lists,\narrays, numbers, and strings as arguments and return types. You might want to experiment with\nthe examples on the Py4CL GitHub page that let you directly call Python libraries without writing\nwrappers. When I write code for my own projects I try to make code as simple as possible so when\nI need to later revisit my own code it is immediately obvious what it is doing. Since I have been\nusing Common Lisp for almost 40 years, I often find myself reusing bits of my own old code and I\noptimize for making this as easy as possible. In other words I favor readability over “clever” code.\nProject Structure, Building the Python Wrapper, and\nRunning an Example\nThe packaging of the Lisp code for my spacy-py4cl package is simple. Here is the listing of\npackage.lisp for this project:\n1\n;;;; package.lisp\n2\n3\n(defpackage #:spacy-py4cl\n4\n(:use #:cl #:py4cl)\n5\n(:export #:nlp))\nListing of spacy-py4cl.asd:\n⁷¹https://github.com/bendudson/py4cl/\n\nUsing the PY4CL Library to Embed Python in Common Lisp\n169\n1\n;;;; spacy-py4cl.asd\n2\n3\n(asdf:defsystem #:spacy-py4cl\n4\n:description \"Use py4cl to use Python spaCy library embedded in Common Lisp\"\n5\n:author \"Mark Watson <markw@markwatson.com>\"\n6\n:license \"Apache 2\"\n7\n:depends-on (#:py4cl)\n8\n:serial t\n9\n:components ((:file \"package\")\n10\n(:file \"spacy-py4cl\")))\nYou need to run a Python setup procedure to install the Python wrapper for space-py4cl on your\nsystem. Some output is removed for conciseness:\n1\n$ cd loving-common-lisp/src/spacy-py4cl\n2\n$ cd PYTHON_SPACY_SETUP_install/spacystub\n3\n$ pip install -U spacy\n4\n$ python -m spacy download en\n5\n$ python setup.py install\n6\nrunning install\n7\nrunning build\n8\nrunning build_py\n9\nrunning install_lib\n10\nrunning install_egg_info\n11\nWriting /Users/markw/bin/anaconda3/lib/python3.7/site-packages/spacystub-0.21-py3.7.\\\n12\negg-info\nYou only need to do this once unless you update to a later version of Python on your system.\nIf you are not familiar with Python, it is worth looking at the wrapper implementation, otherwise\nskip the next few paragraphs.\n$ ls -R PYTHON_SPACY_SETUP_install\nspacystub\nPYTHON_SPACY_SETUP_install/spacystub:\nREADME.md\nsetup.py\nspacystub\nPYTHON_SPACY_SETUP_install/spacystub/build/lib:\nspacystub\nPYTHON_SPACY_SETUP_install/spacystub/spacystub:\nparse.py\n\nUsing the PY4CL Library to Embed Python in Common Lisp\n170\nHere is the implementation of setup.py that specifies how to build and install the wrapper globally\nfor use on your system:\n1\nfrom distutils.core import setup\n2\n3\nsetup(name='spacystub',\n4\nversion='0.21',\n5\npackages=['spacystub'],\n6\nlicense='Apache 2',\n7\npy_modules=['pystub'],\n8\nlong_description=open('README.md').read())\nThe definition of the library in file PYTHON_SPACY_SETUP_install/spacystub/spacystub/parse.py:\n1\nimport spacy\n2\n3\nnlp = spacy.load(\"en\")\n4\n5\ndef parse(text):\n6\ndoc = nlp(text)\n7\nresponse = {}\n8\nresponse['entities'] = [(ent.text, ent.start_char, ent.end_char, ent.label_) for e\\\n9\nnt in doc.ents]\n10\nresponse['tokens'] = [token.text for token in doc]\n11\nreturn [response['tokens'], response['entities']]\nHere is a Common Lisp repl session showing you how to use the library implemented in the next\nsection:\n1\n$ ccl\n2\nClozure Common Lisp Version 1.12\nDarwinX8664\n3\n4\nFor more information about CCL, please see http://ccl.clozure.com.\n5\n6\nCCL is free software.\nIt is distributed under the terms of the Apache Licence, Vers\\\n7\nion 2.0.\n8\n? (ql:quickload \"spacy-py4cl\")\n9\nTo load \"spacy-py4cl\":\n10\nLoad 1 ASDF system:\n11\nspacy-py4cl\n12\n; Loading \"spacy-py4cl\"\n13\n[package spacy-py4cl]\n\nUsing the PY4CL Library to Embed Python in Common Lisp\n171\n14\n(\"spacy-py4cl\")\n15\n? (spacy-py4cl:nlp \"The President of Mexico went to Canada\")\n16\n#(#(\"The\" \"President\" \"of\" \"Mexico\" \"went\" \"to\" \"Canada\") #((\"Mexico\" 17 23 \"GPE\") (\\\n17\n\"Canada\" 32 38 \"GPE\")))\n18\n? (spacy-py4cl:nlp \"Bill Clinton bought a red car. He drove it to the beach.\")\n19\n#(#(\"Bill\" \"Clinton\" \"bought\" \"a\" \"red\" \"car\" \".\" \"He\" \"drove\" \"it\" \"to\" \"the\" \"beac\\\n20\nh\" \".\") #((\"Bill Clinton\" 0 12 \"PERSON\")))\nEntities in text are identified with the starting and ending character indices that refer to the input\nstring. For example, the entity “Mexico” starts at character position 17 and character index 23 is the\ncharacter after the entity name in the input string. The entity type “GPE” refers to a country name\nand “PERSON” refers to a person’s name in the input text.\nImplementation of spacy-py4cl\nThe Common Lisp implementation for this package is simple. In line 5 the call to py4cl:python-exec\nstarts a process to run Python and imports the function parse from my Python wrapper. The call\nto py4cl:import-function in line 6 finds a function named “parse” in the attached Python process\nand generates a Common Lisp function with the same name that handles calling into Python and\nconverting handling the returned values to Common Lisp values:\n1\n;;;; spacy-py4cl.lisp\n2\n3\n(in-package #:spacy-py4cl)\n4\n5\n(py4cl:python-exec \"from spacystub.parse import parse\")\n6\n(py4cl:import-function \"parse\")\n7\n8\n(defun nlp (text)\n9\n(parse text))\nWhile it is possible to call Python libraries directly using Py4CL, when I need to frequently use\nPython libraries like spaCY, TensorFlow, fast.ai, etc. in Common Lisp, I like to use wrappers that use\nsimple as possible data types and APIs to communicate between a Common Lisp process and the\nspawned Python process.\nTrouble Shooting Possible Problems - Skip if this\nExample Works on Your System\nWhen you install my wrapper library in Python on the command line whatever your shell if (bash,\nzsh, etc.) you should then try to import the library in a Python repl:\n\nUsing the PY4CL Library to Embed Python in Common Lisp\n172\n1\n$ python\n2\nPython 3.7.4 (default, Aug 13 2019, 15:17:50)\n3\n[Clang 4.0.1 (tags/RELEASE_401/final)] :: Anaconda, Inc. on darwin\n4\nType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n5\n>>> from spacystub.parse import parse\n6\n>>> parse(\"John Smith is a Democrat\")\n7\n[['John', 'Smith', 'is', 'a', 'Democrat'], [('John Smith', 0, 10, 'PERSON'), ('Democ\\\n8\nrat', 16, 24, 'NORP')]]\n9\n>>>\nIf this works and the Common Lisp library spacy-py4cl does not, then make sure you are starting\nyour Common Lisp program or running a Common Lisp repl with the same Python installation (if\nyou have Quicklisp installed, then you also have the package uiop installed):\n1\n$ which python\n2\n/Users/markw/bin/anaconda3/bin/python\n3\n$ sbcl\n4\nThis is SBCL 2.0.2, an implementation of ANSI Common Lisp.\n5\n* (uiop:run-program \"which python\" :output :string)\n6\n\"/Users/markw/bin/anaconda3/bin/python\"\n7\nnil\n8\n0\n9\n*\nIf you run Common Lisp in an IDE (for example in LispWorks’ IDE or VSCode with a Common Lisp\nplugin) make sure you start the IDE from the command line so your PATH environment variable\nwill be set as it is in our bash or zsh shell.\nWrap-up for Using Py4CL\nWhile I prefer Common Lisp for general development and also AI research, there are useful Python\nlibraries that I want to integrate into my projects. I hope that the last chapter and this chapter provide\nyou with two solid approaches for you to use in your own work to take advantage of Python libraries.\n\nSemantic Web and Linked Data\nI have written two previous books on the semantic web and linked data and most of my programming\nbooks have semantic web examples. Please note that the background material here on the semantic\nweb standards RDF, RDFS, and SPARQL is shared with my book Practical Artificial Intelligence\nProgramming With Java⁷² so if you have read that book then the first several pages of this chapter\nwill seem familiar.\nConstruction of Knowledge Graphs, as we will do in later chapters, is a core technology at many\ncorporations and organizations to prevent data silos where different database systems are poorly\nconnected and not as useful in combination than they could be. The use of RDF data stores is a\npowerful technique for data interoperability within organizations. Semantic Web standards like\nRDF, RDFS, and SPARQL support both building Knowledge Graphs and also key technologies for\nautomating the collection and use of web data.\nI worked as a contractor at Google on an internal Knowledge Graph project and I currently work at\nOlive AI⁷³ on their Knowledge Graph team.\nThe semantic web is intended to provide a massive linked set of data for use by software systems\njust as the World Wide Web provides a massive collection of linked web pages for human reading\nand browsing. The semantic web is like the web in that anyone can generate any content that they\nwant. This freedom to publish anything works for the web because we use our ability to understand\nnatural language to interpret what we read – and often to dismiss material that based upon our own\nknowledge we consider to be incorrect.\nSemantic web and linked data technologies are also useful for smaller amounts of data, an example\nbeing a Knowledge Graph containing information for a business. We will further explore Knowledge\nGraphs in the next two chapters.\nThe core concept for the semantic web is data integration and use from different sources. As we will\nsoon see, the tools for implementing the semantic web are designed for encoding data and sharing\ndata from many different sources.\nI cover the semantic web in this book because I believe that semantic web technologies are\ncomplementary to AI systems for gathering and processing data on the web. As more web pages\nare generated by applications (as opposed to simply showing static HTML files) it becomes easier to\nproduce both HTML for human readers and semantic data for software agents.\nThere are several very good semantic web toolkits for the Java language and platform. Here we use\nApache Jena because it is what I often use in my own work and I believe that it is a good starting\ntechnology for your first experiments with semantic web technologies. This chapter provides an\n⁷²https://leanpub.com/javaai\n⁷³https://oliveai.com\n\nSemantic Web and Linked Data\n174\nincomplete coverage of semantic web technologies and is intended as a gentle introduction to a\nfew useful techniques and how to implement those techniques in Java. This chapter is the start of\na journey in the technology that I think is as important as technologies like deep learning that get\nmore public mindshare.\nThe following figure shows a layered hierarchy of data models that are used to implement semantic\nweb applications. To design and implement these applications we need to think in terms of physical\nmodels (storage and access of RDF, RDFS, and perhaps OWL data), logical models (how we use\nRDF and RDFS to define relationships between data represented as unique URIs and string literals\nand how we logically combine data from different sources) and conceptual modeling (higher level\nknowledge representation and reasoning using OWL). Originally RDF data was serialized as XML\ndata but other formats have become much more popular because they are easier to read and manually\ncreate. The top three layers in the figure might be represented as XML, or as LD-JSON (linked data\nJSON) or formats like N-Triples and N3 that we will use later.\nSemantic Web Data Models\nResource Description Framework (RDF) Data Model\nThe Resource Description Framework (RDF) is used to encode information and the RDF Schema\n(RDFS) facilitates using data with different RDF encodings without the need to convert one set\nof schemas to another. Later, using OWL we can simply declare that one predicate is the same as\nanother, that is, one predicate is a sub-predicate of another (e.g., a property containsCity can be\ndeclared to be a sub-property of containsPlace so if something contains a city then it also contains\na place), etc. The predicate part of an RDF statement often refers to a property.\n\nSemantic Web and Linked Data\n175\nRDF data was originally encoded as XML and intended for automated processing. In this chapter we\nwill use two simple to read formats called “N-Triples” and “N3.” Apache Jena can be used to convert\nbetween all RDF formats so we might as well use formats that are easier to read and understand.\nRDF data consists of a set of triple values:\n• subject\n• predicate\n• object\nSome of my work with semantic web technologies deals with processing news stories, extracting\nsemantic information from the text, and storing it in RDF. I will use this application domain for the\nexamples in this chapter and the next chapter when we implement code to automatically generate\nRDF for Knowledge Graphs. I deal with triples like:\n• subject: a URL (or URI) of a news article.\n• predicate: a relation like “containsPerson”.\n• object: a literal value like “Bill Clinton” or a URI representing Bill Clinton.\nIn the next chapter we will use the entity recognition library we developed in an earlier chapter to\ncreate RDF from text input.\nWe will use either URIs or string literals as values for objects. We will always use URIs for\nrepresenting subjects and predicates. In any case URIs are usually preferred to string literals. We\nwill see an example of this preferred use but first we need to learn the N-Triple and N3 RDF formats.\nI proposed the idea that RDF was more flexible than Object Modeling in programming languages,\nrelational databases, and XML with schemas. If we can tag new attributes on the fly to existing\ndata, how do we prevent what I might call “data chaos” as we modify existing data sources? It turns\nout that the solution to this problem is also the solution for encoding real semantics (or meaning)\nwith data: we usually use unique URIs for RDF subjects, predicates, and objects, and usually with\na preference for not using string literals. The definitions of predicates are tied to a namespace and\nlater with OWL we will state the equivalence of predicates in different namespaces with the same\nsemantic meaning. I will try to make this idea more clear with some examples and Wikipedia has a\ngood writeup on RDF⁷⁴.\nAny part of a triple (subject, predicate, or object) is either a URI or a string literal. URIs encode\nnamespaces. For example, the containsPerson predicate in the last example could be written as:\nhttp://knowledgebooks.com/ontology/#containsPerson\nThe first part of this URI is considered to be the namespace for this predicate “containsPerson.”\nWhen different RDF triples use this same predicate, this is some assurance to us that all users of this\n⁷⁴https://en.wikipedia.org/wiki/Resource_Description_Framework\n\nSemantic Web and Linked Data\n176\npredicate understand to the same meaning. Furthermore, we will see later that we can use RDFS to\nstate equivalency between this predicate (in the namespace http://knowledgebooks.com/ontology/)\nwith predicates represented by different URIs used in other data sources. In an “artificial intelligence”\nsense, software that we write does not understand predicates like “containsCity”, “containsPerson”,\nor “isLocation” in the way that a human reader can by combining understood common meanings\nfor the words “contains”, “city”, “is”, “person”, and “location” but for many interesting and useful\ntypes of applications that is fine as long as the predicate is used consistently. We will see shortly\nthat we can define abbreviation prefixes for namespaces which makes RDF and RDFS files shorter\nand easier to read.\nThe Jena library supports most serialization formats for RDF:\n• Turtle\n• N3\n• N-Triples\n• NQuads\n• TriG\n• JSON-LD\n• RDF/XML\n• RDF/JSON\n• TriX\n• RDF Binary\nA statement in N-Triple format consists of three URIs (two URIs and a string literals for the object)\nfollowed by a period to end the statement. While statements are often written one per line in a source\nfile they can be broken across lines; it is the ending period which marks the end of a statement. The\nstandard file extension for N-Triple format files is *.nt and the standard format for N3 format files\nis *.n3.\nMy preference is to use N-Triple format files as output from programs that I write to save data as\nRDF. N-Triple files don’t use any abbreviations and each RDF statement is self-contained. I often\nuse tools like the command line commands in Jena or RDF4J to convert N-Triple files to N3 or other\nformats if I will be reading them or even hand editing them. Here is an example using the N3 syntax:\n@prefix kb:\n<http://knowledgebooks.com/ontology#>\n<http://news.com/201234/> kb:containsCountry \"China\" .\nThe N3 format adds prefixes (abbreviations) to the N-Triple format. In practice it would be better to\nuse the URI http://dbpedia.org/resource/China instead of the literal value “China.”\nHere we see the use of an abbreviation prefix “kb:” for the namespace for my company Knowledge-\nBooks.com ontologies. The first term in the RDF statement (the subject) is the URI of a news article.\nThe second term (the predicate) is “containsCountry” in the “kb:” namespace. The last item in the\n\nSemantic Web and Linked Data\n177\nstatement (the object) is a string literal “China.” I would describe this RDF statement in English as,\n“The news article at URI http://news.com/201234 mentions the country China.”\nThis was a very simple N3 example which we will expand to show additional features of the N3\nnotation. As another example, let’s look at the case if this news article also mentions the USA. Instead\nof adding a whole new statement like this we can combine them using N3 notation. Here we have\ntwo separate RDF statements:\n@prefix kb:\n<http://knowledgebooks.com/ontology#> .\n<http://news.com/201234/>\nkb:containsCountry\n<http://dbpedia.org/resource/China>\n.\n<http://news.com/201234/>\nkb:containsCountry\n<http://dbpedia.org/resource/United_States>\n.\nWe can collapse multiple RDF statements that share the same subject and optionally the same\npredicate:\n@prefix kb:\n<http://knowledgebooks.com/ontology#> .\n<http://news.com/201234/>\nkb:containsCountry\n<http://dbpedia.org/resource/China> ,\n<http://dbpedia.org/resource/United_States>\n.\nThe indentation and placement on separate lines is arbitrary - use whatever style you like that is\nreadable. We can also add in additional predicates that use the same subject (I am going to use string\nliterals here instead of URIs for objects to make the following example more concise but in practice\nprefer using URIs):\n@prefix kb:\n<http://knowledgebooks.com/ontology#> .\n<http://news.com/201234/>\nkb:containsCountry \"China\" ,\n\"USA\" .\nkb:containsOrganization \"United Nations\" ;\nkb:containsPerson \"Ban Ki-moon\" , \"Gordon Brown\" ,\n\"Hu Jintao\" , \"George W. Bush\" ,\n\"Pervez Musharraf\" ,\n\"Vladimir Putin\" ,\n\"Mahmoud Ahmadinejad\" .\n\nSemantic Web and Linked Data\n178\nThis single N3 statement represents ten individual RDF triples. Each section defining triples with\nthe same subject and predicate have objects separated by commas and ending with a period. Please\nnote that whatever RDF storage system you use (we will be using Jena) it makes no difference if we\nload RDF as XML, N-Triple, of N3 format files: internally subject, predicate, and object triples are\nstored in the same way and are used in the same way. RDF triples in a data store represent directed\ngraphs that may not all be connected.\nI promised you that the data in RDF data stores was easy to extend. As an example, let us assume\nthat we have written software that is able to read online news articles and create RDF data that\ncaptures some of the semantics in the articles. If we extend our program to also recognize dates\nwhen the articles are published, we can simply reprocess articles and for each article add a triple to\nour RDF data store using a form like:\n@prefix kb:\n<http://knowledgebooks.com/ontology#> .\n<http://news.com/201234/> kb:datePublished \"2008-05-11\" .\nHere we just represent the date as a string. We can add a type to the object representing a specific\ndate:\n@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n@prefix kb:\n<http://knowledgebooks.com/ontology#> .\n<http://news.com/201234/> kb:datePublished \"2008-05-11\"^^xsd:date .\nFurthermore, if we do not have dates for all news articles that is often acceptable because when\nconstructing SPARQL queries you can match optional patterns. If for example you are looking up\narticles on a specific subject then some results may have a publication date attached to the results\nfor that article and some might not. In practice RDF supports types and we would use a date type as\nseen in the last example, not a string. However, in designing the example programs for this chapter\nI decided to simplify our representation of URIs and often use string literals as simple Java strings.\nFor many applications this isn’t a real limitation.\nExtending RDF with RDF Schema\nRDF Schema (RDFS) supports the definition of classes and properties based on set inclusion. In\nRDFS classes and properties are orthogonal. Let’s start with looking at an example using additional\nnamespaces:\n\nSemantic Web and Linked Data\n179\n@prefix kb:\n<http://knowledgebooks.com/ontology#> .\n@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n@prefix rdfs:\n<http://www.w3.org/2000/01/rdf-schema#>\n@prefix dbo: <http://dbpedia.org/ontology/>\n<http://news.com/201234/>\nkb:containsCountry\n<http://dbpedia.org/resource/China>\n.\n<http://news.com/201234/>\nkb:containsCountry\n<http://dbpedia.org/resource/United_States>\n.\n<http://dbpedia.org/resource/China>\nrdfs:label \"China\"@en,\nrdf:type dbo:Place ,\nrdf:type dbo:Country .\nBecause the semantic web is intended to be processed automatically by software systems it is\nencoded as RDF. There is a problem that must be solved in implementing and using the semantic\nweb: everyone who publishes semantic web data is free to create their own RDF schemas for storing\ndata; for example, there is usually no single standard RDF schema definition for topics like news\nstories and stock market data. The SKOS⁷⁵ is a namespace containing standard schemas and the\nmost widely used standard is schema.org⁷⁶. Understanding the ways of integrating different data\nsources using different schemas helps to understand the design decisions behind the semantic web\napplications. In this chapter I often use my own schemas in the knowledgebooks.com namespace for\nthe simple examples you see here. When you build your own production systems part of the work is\nsearching through schema.org and SKOS to use standard name spaces and schemas when possible.\nThe use of standard schemas helps when you link internal proprietary Knowledge Graphs used in\norganization with public open data from sources like WikiData⁷⁷ and DBPedia⁷⁸.\nWe will start with an example that is an extension of the example in the last section that also uses\nRDFS. We add a few additional RDF statements:\n⁷⁵https://www.w3.org/2009/08/skos-reference/skos.html\n⁷⁶https://schema.org/docs/schemas.html\n⁷⁷https://www.wikidata.org/wiki/Wikidata:Main_Page\n⁷⁸https://wiki.dbpedia.org/about\n\nSemantic Web and Linked Data\n180\n@prefix kb:\n<http://knowledgebooks.com/ontology#> .\n@prefix rdfs:\n<http://www.w3.org/2000/01/rdf-schema#> .\nkb:containsCity rdfs:subPropertyOf kb:containsPlace .\nkb:containsCountry rdfs:subPropertyOf kb:containsPlace .\nkb:containsState rdfs:subPropertyOf kb:containsPlace .\nThe last three lines declare that:\n• The property containsCity is a sub-property of containsPlace.\n• The property containsCountry is a sub-property of containsPlace.\n• The property containsState is a sub-property of containsPlace.\nWhy is this useful? For at least two reasons:\n• You can query an RDF data store for all triples that use property containsPlace and also match\ntriples with properties equal to containsCity, containsCountry, or containsState. There may not\neven be any triples that explicitly use the property containsPlace.\n• Consider a hypothetical case where you are using two different RDF data stores that use\ndifferent properties for naming cities: cityName and city. You can define cityName to be a\nsub-property of city and then write all queries against the single property name city. This\nremoves the necessity to convert data from different sources to use the same Schema. You can\nalso use OWL to state property and class equivalency.\nIn addition to providing a vocabulary for describing properties and class membership by properties,\nRDFS is also used for logical inference to infer new triples, combine data from different RDF data\nsources, and to allow effective querying of RDF data stores. We will see examples of all of these\nfeatures of RDFS when we later start using the Jena libraries to perform SPARQL queries.\nThe SPARQL Query Language\nSPARQL is a query language used to query RDF data stores. While SPARQL may initially look\nlike SQL, we will see that there are some important differences like support for RDFS and OWL\ninferencing and graph-based instead of relational matching operations. We will cover the basics of\nSPARQL in this section and then see more examples later when we learn how to embed Jena in Java\napplications, and see more examples in the last chapter Knowledge Graph Navigator.\nWe will use the N3 format RDF file test_data/news.n3 for the examples. I created this file\nautomatically by spidering Reuters news stories on the news.yahoo.com web site and automatically\nextracting named entities from the text of the articles. We saw techniques for extracting named\nentities from text in earlier chapters. In this chapter we use these sample RDF files.\nYou have already seen snippets of this file and I list the entire file here for reference, edited to fit\nline width: you may find the file news.n3 easier to read if you are at your computer and open the\nfile in a text editor so you will not be limited to what fits on a book page:\n\nSemantic Web and Linked Data\n181\n@prefix kb:\n<http://knowledgebooks.com/ontology#> .\n@prefix rdfs:\n<http://www.w3.org/2000/01/rdf-schema#> .\nkb:containsCity rdfs:subPropertyOf kb:containsPlace .\nkb:containsCountry rdfs:subPropertyOf kb:containsPlace .\nkb:containsState rdfs:subPropertyOf kb:containsPlace .\n<http://yahoo.com/20080616/usa_flooding_dc_16/>\nkb:containsCity \"Burlington\" , \"Denver\" ,\n\"St. Paul\" ,\" Chicago\" ,\n\"Quincy\" , \"CHICAGO\" ,\n\"Iowa City\" ;\nkb:containsRegion \"U.S. Midwest\" , \"Midwest\" ;\nkb:containsCountry \"United States\" , \"Japan\" ;\nkb:containsState \"Minnesota\" , \"Illinois\" ,\n\"Mississippi\" , \"Iowa\" ;\nkb:containsOrganization \"National Guard\" ,\n\"U.S. Department of Agriculture\" ,\n\"White House\" ,\n\"Chicago Board of Trade\" ,\n\"Department of Transportation\" ;\nkb:containsPerson \"Dena Gray-Fisher\" ,\n\"Donald Miller\" ,\n\"Glenn Hollander\" ,\n\"Rich Feltes\" ,\n\"George W. Bush\" ;\nkb:containsIndustryTerm \"food inflation\" , \"food\" ,\n\"finance ministers\" ,\n\"oil\" .\n<http://yahoo.com/78325/ts_nm/usa_politics_dc_2/>\nkb:containsCity \"Washington\" , \"Baghdad\" ,\n\"Arlington\" , \"Flint\" ;\nkb:containsCountry \"United States\" ,\n\"Afghanistan\" ,\n\"Iraq\" ;\nkb:containsState \"Illinois\" , \"Virginia\" ,\n\"Arizona\" , \"Michigan\" ;\nkb:containsOrganization \"White House\" ,\n\"Obama administration\" ,\n\"Iraqi government\" ;\n\nSemantic Web and Linked Data\n182\nkb:containsPerson \"David Petraeus\" ,\n\"John McCain\" ,\n\"Hoshiyar Zebari\" ,\n\"Barack Obama\" ,\n\"George W. Bush\" ,\n\"Carly Fiorina\" ;\nkb:containsIndustryTerm \"oil prices\" .\n<http://yahoo.com/10944/ts_nm/worldleaders_dc_1/>\nkb:containsCity \"WASHINGTON\" ;\nkb:containsCountry \"United States\" , \"Pakistan\" ,\n\"Islamic Republic of Iran\" ;\nkb:containsState \"Maryland\" ;\nkb:containsOrganization \"University of Maryland\" ,\n\"United Nations\" ;\nkb:containsPerson \"Ban Ki-moon\" , \"Gordon Brown\" ,\n\"Hu Jintao\" , \"George W. Bush\" ,\n\"Pervez Musharraf\" ,\n\"Vladimir Putin\" ,\n\"Steven Kull\" ,\n\"Mahmoud Ahmadinejad\" .\n<http://yahoo.com/10622/global_economy_dc_4/>\nkb:containsCity \"Sao Paulo\" , \"Kuala Lumpur\" ;\nkb:containsRegion \"Midwest\" ;\nkb:containsCountry \"United States\" , \"Britain\" ,\n\"Saudi Arabia\" , \"Spain\" ,\n\"Italy\" , India\" ,\n\"\"France\" , \"Canada\" ,\n\"Russia\" , \"Germany\" , \"China\" ,\n\"Japan\" , \"South Korea\" ;\nkb:containsOrganization \"Federal Reserve Bank\" ,\n\"European Union\" ,\n\"European Central Bank\" ,\n\"European Commission\" ;\nkb:containsPerson \"Lee Myung-bak\" , \"Rajat Nag\" ,\n\"Luiz Inacio Lula da Silva\" ,\n\"Jeffrey Lacker\" ;\nkb:containsCompany \"Development Bank Managing\" ,\n\"Reuters\" ,\n\"Richmond Federal Reserve Bank\" ;\nkb:containsIndustryTerm \"central bank\" , \"food\" ,\n\"energy costs\" ,\n\nSemantic Web and Linked Data\n183\n\"finance ministers\" ,\n\"crude oil prices\" ,\n\"oil prices\" ,\n\"oil shock\" ,\n\"food prices\" ,\n\"Finance ministers\" ,\n\"Oil prices\" , \"oil\" .\nIn the following examples, we will use the main method in the class JenaApi (developed in the\nnext section) that allows us to load multiple RDF input files and then to interactively enter SPARQL\nqueries.\nWe will start with a simple SPARQL query for subjects (news article URLs) and objects (matching\ncountries) with the value for the predicate equal to containsCountry. Variables in queries start with\na question mark character and can have any names:\nSELECT ?subject ?object\nWHERE {\n?subject\n<http://knowledgebooks.com/ontology#containsCountry>\n?object .\n}\nIt is important for you to understand what is happening when we apply the last SPARQL query to\nour sample data. Conceptually, all the triples in the sample data are scanned, keeping the ones where\nthe predicate part of a triple is equal to http://knowledgebooks.com/ontology#containsCountry.\nIn practice RDF data stores supporting SPARQL queries index RDF data so a complete scan of the\nsample data is not required. This is analogous to relational databases where indices are created to\navoid needing to perform complete scans of database tables.\nIn practice, when you are exploring a Knowledge Graph like DBPedia or WikiData (that are just\nvery large collections of RDF triples), you might run a query and discover a useful or interesting\nentity URI in the triple store, then drill down to find out more about the entity. In a later chapter\nKnowledge Graph Navigator we attempt to automate this exploration process using the DBPedia\ndata as a Knowledge Graph.\nWe will be using the same code to access the small example of RDF statements in our sample data\nas we will for accessing DBPedia or WikiData.\nWe can make this last query easier to read and reduce the chance of misspelling errors by using a\nnamespace prefix:\n\nSemantic Web and Linked Data\n184\nPREFIX kb:\n<http://knowledgebooks.com/ontology#>\nSELECT ?subject ?object\nWHERE {\n?subject kb:containsCountry ?object .\n}\nLater in the chapter Knowledge Graph Navigator we will write an application that automatically\ngenerates SPARQL queries for the DBPedia public knowledge Graph. These queries will be be more\ncomplex than the simpler examples here. Reading this chapter before Knowledge Graph Navigator\nis recommended.\nCase Study: Using SPARQL to Find Information about\nBoard of Directors Members of Corporations and\nOrganizations\nBefore we write software to automate the process of using SPARQL queries to find information\non DBPedia, let’s perform a few manual queries for finding information on board of directors of\ncorportations. To start with, we would like to find an RDF property that indicates board membership.\nThere is a common expression for finding information on the web using search engines and also for\nusing SPARQL queries: “follow your nose,” that is, when you see something interesting, dig down\nwith more queries on whatever interests you.\nSELECT DISTINCT ?s\nWHERE {\n?s ?p \"Board of Directors\"@en .\nFILTER (?p IN (<http://www.w3.org/2000/01/rdf-schema#label>, <http://xmlns.com/f\\\noaf/0.1/name>) && !regex(str(?s), \"category\", \"i\"))\n}\nWe will find the property:\nhttp://dbpedia.org/resource/Board_of_Directors\nRDF\nselect ?s ?p { ?s ?p <http://dbpedia.org/resource/Board_of_Directors> } limit 6\n\nSemantic Web and Linked Data\n185\ns\np\nhttp://en.wikipedia.org/wiki/Board_of_Directors\nhttp://xmlns.com/foaf/0.1/primaryTop\\\nic\nhttp://dbpedia.org/resource/Lynn_D._Stewart_(businessman)\nhttp://dbpedia.org/ontolog\\\ny/board\nhttp://dbpedia.org/resource/Advance_America_Cash_Advance\nhttp://dbpedia.org/ontology\\\n/keyPerson\nhttp://dbpedia.org/resource/Railways_of_Slovak_Republic\nhttp://dbpedia.org/ontology/\\\nkeyPerson\nhttp://dbpedia.org/resource/Divine_Word_University_of_Tacloban__DWU_Jubilee_Foundati\\\non,_Inc.__1\nhttp://dbpedia.org/ontology/keyPerson\nhttp://dbpedia.org/resource/Mathys_Medical\nThe property http://dbpedia.org/ontology/board is what we are looking for. Let’s keep “following\nour nose” to find examples of board members and the companies they server:\nselect ?person ?company { ?person <http://dbpedia.org/ontology/board> ?company} limi\\\nt 6\nThe results are:\nperson\ncompany\nhttp://dbpedia.org/resource/Matthew_Buckland\nhttp://dbpedia.org/resource/Creative_Co\\\nmmons\nhttp://dbpedia.org/resource/Jimmy_Wales\nhttp://dbpedia.org/resource/Creative_Commons\nhttp://dbpedia.org/resource/Nabeel_Rajab\nhttp://dbpedia.org/resource/Human_Rights_Wa\\\ntch\nhttp://dbpedia.org/resource/Vincent_Tewson\nhttp://dbpedia.org/resource/International\\\n_Confederation_of_Free_Trade_Unions\nhttp://dbpedia.org/resource/William_T._Young\nhttp://dbpedia.org/resource/KFC\nhttp://dbpedia.org/resource/Colonel_Sanders\nhttp://dbpedia.org/resource/KFC\nLet’s see what information we can find on the founder of WikiPedi Jimmy Wales:\nselect ?p ?o { <http://dbpedia.org/resource/Jimmy_Wales> ?p ?o } limit 200\nA few of the many results are:\n\nSemantic Web and Linked Data\n186\np\no\nhttp://www.w3.org/1999/02/22-rdf-syntax-ns#type\nhttp://www.w3.org/2002/07/owl#Thing\nhttp://www.w3.org/1999/02/22-rdf-syntax-ns#type\nhttp://xmlns.com/foaf/0.1/Person\nhttp://www.w3.org/1999/02/22-rdf-syntax-ns#type\nhttp://dbpedia.org/ontology/Person\nhttp://www.w3.org/1999/02/22-rdf-syntax-ns#type\nhttp://www.wikidata.org/entity/Q2156\\\n27\nhttp://www.w3.org/1999/02/22-rdf-syntax-ns#type\nhttp://www.wikidata.org/entity/Q2422\\\n9398\nhttp://www.w3.org/1999/02/22-rdf-syntax-ns#type\nhttp://www.wikidata.org/entity/Q5\nhttp://www.w3.org/1999/02/22-rdf-syntax-ns#type\nhttp://dbpedia.org/ontology/Agent\nhttp://www.w3.org/1999/02/22-rdf-syntax-ns#type\nhttp://schema.org/Person\nhttp://www.w3.org/1999/02/22-rdf-syntax-ns#type\nhttp://dbpedia.org/class/yago/Wikica\\\ntAmericanComputerScientists\nhttp://www.w3.org/1999/02/22-rdf-syntax-ns#type\nhttp://dbpedia.org/class/yago/Wikica\\\ntAmericanExpatriatesInTheUnitedKingdom\nhttp://www.w3.org/2000/01/rdf-schema#label\n\"Jimmy Wales\"@en\nInstalling the Apache Jena Fuseki RDF Server\nTBD\nI have a github repository mark-watson/fuseki-semantic-web-dev-setup⁷⁹mthat you shoud clone:\n1\ngit clone https://github.com/mark-watson/fuseki-semantic-web-dev-setup.git\n2\ncd fuseki-semantic-web-dev-setup\n3\n./fuseki-server --file RDF/sample_news.nt /news\nThis will run the SPARQL server Fuseki locally on your laptop and the default graph is “news” and\nyou will see output like:\n⁷⁹https://github.com/mark-watson/fuseki-semantic-web-dev-setup\n\nSemantic Web and Linked Data\n187\n1\n$ ./fuseki-server --file RDF/sample_news.nt /news\n2\n[2020-11-07 09:31:13] Server\nINFO\nDataset: in-memory: load file: RDF/sample_new\\\n3\ns.nt\n4\n[2020-11-07 09:31:14] Server\nINFO\nRunning in read-only mode for /news\n5\n[2020-11-07 09:31:14] Server\nINFO\nApache Jena Fuseki 3.16.0\n6\n[2020-11-07 09:31:14] Config\nINFO\nFUSEKI_HOME=/Users/markw/GITHUB/fuseki-semant\\\n7\nic-web-dev-setup/.\n8\n[2020-11-07 09:31:14] Config\nINFO\nFUSEKI_BASE=/Users/markw/GITHUB/fuseki-semant\\\n9\nic-web-dev-setup/run\n10\n[2020-11-07 09:31:14] Config\nINFO\nShiro file: file:///Users/markw/GITHUB/fuseki\\\n11\n-semantic-web-dev-setup/run/shiro.ini\n12\n[2020-11-07 09:31:15] Server\nINFO\nDataset Type: in-memory, with files loaded\n13\n[2020-11-07 09:31:15] Server\nINFO\nPath = /news\n14\n[2020-11-07 09:31:15] Server\nINFO\nSystem\n15\n[2020-11-07 09:31:15] Server\nINFO\nMemory: 4.0 GiB\n16\n[2020-11-07 09:31:15] Server\nINFO\nJava:\n14.0.1\n17\n[2020-11-07 09:31:15] Server\nINFO\nOS:\nMac OS X 10.15.7 x86_64\n18\n[2020-11-07 09:31:15] Server\nINFO\nPID:\n3855\n19\n[2020-11-07 09:31:15] Server\nINFO\nStarted 2020/11/07 09:31:15 MST on port 3030\nYou can access a web interface for SPARQL queries by accessing localhost:3030 or http:127.0.0.1:3030.\nCommon Lisp Client Examples for the Apache Jena\nFuseki RDF Server\nLater in the chapter “Knowledge Graph Navigator” we will develop a simple Common Lisp SPARQL\nquery library and use it for querying DBPedia. Here we will use it to query our local Fuseki server.\n1\n$ sbcl\n2\nThis is SBCL 2.0.7, an implementation of ANSI Common Lisp.\n3\nMore information about SBCL is available at <http://www.sbcl.org/>.\n4\n5\nSBCL is free software, provided as is, with absolutely no warranty.\n6\nIt is mostly in the public domain; some portions are provided under\n7\nBSD-style licenses.\nSee the CREDITS and COPYING files in the\n8\ndistribution for more information.\n9\n10\n#P\"/Users/markw/quicklisp/setup.lisp\"\n11\n\"starting up quicklisp\"\n12\n* (quicklisp:quickload \"sparql\")\n13\nTo load \"sparql\":\n\nSemantic Web and Linked Data\n188\n14\nLoad 1 ASDF system:\n15\nsparql\n16\n; Loading \"sparql\"\n17\n...............\n18\n(\"sparql\")\n19\n* (sparql::fuseki \"select ?s ?p ?o { ?s ?p ?o } limit 20\")\n20\n(((:s \"http://kbsportal.com/trout_season/\")\n21\n(:p \"http://knowledgebooks.com/ontology/#storyType\")\n22\n(:o \"http://knowledgebooks.com/ontology/#recreation\"))\n23\n((:s \"http://kbsportal.com/trout_season/\")\n24\n(:p \"http://knowledgebooks.com/ontology/#storyType\")\n25\n(:o \"http://knowledgebooks.com/ontology/#sports\"))\n26\n((:s \"http://kbsportal.com/bear_mountain_fire/\")\n27\n(:p \"http://knowledgebooks.com/ontology/#storyType\")\n28\n(:o \"http://knowledgebooks.com/ontology/#disaster\"))\n29\n((:s \"http://kbsportal.com/bear_mountain_fire/\")\n30\n(:p \"http://knowledgebooks.com/ontology/#summary\")\n31\n(:o \"The fire on Bear Mountain was caused by lightening\"))\n32\n((:s \"http://kbsportal.com/jc_basketball/\")\n33\n(:p \"http://knowledgebooks.com/ontology/#storyType\")\n34\n(:o \"http://knowledgebooks.com/ontology/#sports\"))\n35\n((:s \"http://kbsportal.com/oak_creek_flooding/\")\n36\n(:p \"http://knowledgebooks.com/ontology/#storyType\")\n37\n(:o \"http://knowledgebooks.com/ontology/#disaster\"))\n38\n((:s \"http://kbsportal.com/oak_creek_flooding/\")\n39\n(:p \"http://knowledgebooks.com/ontology/#summary\")\n40\n(:o \"Oak Creek flooded last week affecting 5 businesses\")))\nHere is an example of using the same library to query the public DBPedia SPARQL endpoint (most\noutput is not shown):\n1\n* (sparql:dbpedia \"select ?s ?p { ?s ?p \\\"Bill Gates\\\"@en }\")\n2\n3\n(\"ndbpeia SPARQL:n\" \"select ?s ?p { ?s ?p \\\"Bill Gates\\\"@en }\" \"n\")\n4\n(((:s \"http://dbpedia.org/resource/Category:Bill_Gates\")\n5\n(:p \"http://www.w3.org/2000/01/rdf-schema#label\"))\n6\n((:s \"http://www.wikidata.org/entity/Q5284\")\n7\n(:p \"http://www.w3.org/2000/01/rdf-schema#label\"))\n8\n((:s \"http://dbpedia.org/resource/Bill_Gates\")\n9\n(:p \"http://xmlns.com/foaf/0.1/name\"))\n10\n)\nThe SPARQL library in the github repository for this book also supports the commercial products\n\nSemantic Web and Linked Data\n189\nAllegroGraph and Stardog RDF servers.\n\nAutomatically Generating Data for\nKnowledge Graphs\nWe develop a complete application. The Knowledge Graph Creator (KGcreator) is a tool for\nautomating the generation of data for Knowledge Graphs from raw text data. We will see how to\ncreate a single standalone executable file using SBCL Common Lisp. The application can also be run\nduring development from a repl. This application also implements a web application interface. In\naddition to the KGcreator application we will close the chapter with a utiity library that processes\na file of RDF in N-Triple format and generates an extention file with triples pulled from DBedia\ndefining URIs found in the input data file.\nData created by KGcreator generates data in two formats:\n• Neo4j graph database format (text format)\n• RDF triples suitable for loading into any linked data/semantic web data store.\nThis example application works by identifying entities in text. Example entity types are people,\ncompanies, country names, city names, broadcast network names, political party names, and\nuniversity names. We saw earlier code for detecting entities in the chapter on natural language\nprocessing (NLP) and we will reuse this code. We will discuss later three strategies for reusing code\nfrom different projects.\nWhen I originally wrote KGCreator I intended to develop a commercial product. I wrote two research\nprototypes, one in Common Lisp (the example in this chapter) and one in Haskell (which I also use\nas an example in my book Haskell Tutorial and Cookbook⁸⁰. I decided to open source both versions\nof KGCreator and if you work with Knowledge Graphs I hope you find KGCreator useful in your\nwork.\nThe following figure shows part of a Neo4j Knowledge Graph created with the example code. This\ngraph has shortened labels in displayed nodes but Neo4j offers a web browser-based console that lets\nyou interactively explore Knowledge Graphs. We don’t cover setting up Neo4j here so please use the\nNeo4j documentation⁸¹. As an introduction to RDF data, the semantic web, and linked data you can\nget free copies of my two books Practical Semantic Web and Linked Data Applications, Common\nLisp Edition⁸² and Practical Semantic Web and Linked Data Applications, Java, Scala, Clojure, and\nJRuby Edition⁸³.\n⁸⁰https://leanpub.com/haskell-cookbook/\n⁸¹https://neo4j.com/docs/operations-manual/current/introduction/\n⁸²http://markwatson.com/opencontentdata/book_lisp.pdf\n⁸³http://markwatson.com/opencontentdata/book_java.pdf\n\nAutomatically Generating Data for Knowledge Graphs\n191\nPart of a Knowledge Graph shown in Neo4j web application console\nHere is a detail view:\nDetail of Neo4j console\nImplementation Notes\nAs seen in the file src /kgcreator/package.lisp this application uses several other packages:\n\nAutomatically Generating Data for Knowledge Graphs\n192\n1\n(defpackage #:kgcreator\n2\n(:use #:cl\n3\n#:entities_dbpedia #:categorize_summarize #:myutils\n4\n#:cl-who #:hunchentoot #:parenscript)\n5\n(:export kgcreator))\nThe implementation of the packages shown on line 3 were in a previous chapter. The packagemyutils\nare mostly miscellaneous string utilities that we won’t look at here; I leave it to you to read the source\ncode.\nAs seen in the configuration file src/kgcreator/kgcreator.asd we split the implementation of the\napplication into four source files:\n1\n;;;; kgcreator.asd\n2\n3\n(asdf:defsystem #:kgcreator\n4\n:description \"Describe plotlib here\"\n5\n:author \"Mark Watson <mark.watson@gmail.com>\"\n6\n:license \"AGPL version 3\"\n7\n:depends-on (#:entities_dbpedia #:categorize_summarize\n8\n#:myutils #:unix-opts #:cl-who\n9\n#:hunchentoot #:parenscript)\n10\n:components\n11\n((:file \"package\")\n12\n(:file \"kgcreator\")\n13\n(:file \"neo4j\")\n14\n(:file \"rdf\")\n15\n(:file \"web\"))\n16\n)\nThe application is separated into four source files:\n• kgcreator.lisp: top level APIs and functionality. Uses the code in neo4j.lisp and rdf.lisp. Later\nwe will generate a standalone application that uses these top level APIs\n• neo4j.lisp: generates Cyper text files that can be imported into Neo4j\n•\n– rdf.lisp: generates RDF text data that can be loaded or imported into RDF data stores\n• web.lisp: a simple web application for running KGCreator\nGenerating RDF Data\nI leave it to you find a tutorial on RDF data on the web, or you can get a PDF for my book “Practical\nSemantic Web and Linked Data Applications, Common Lisp Edition”⁸⁴ and read the tutorial sections\non RDF.\n⁸⁴http://markwatson.com/opencontentdata/book_lisp.pdf\n\nAutomatically Generating Data for Knowledge Graphs\n193\nRDF data is comprised of triples, where the value for each triple are a subject, a predicate, and an\nobject. Subjects are URIs, predicates are usually URIs, and objects are either literal values or URIs.\nHere are two triples written by this example application:\n<http://dbpedia.org/resource/The_Wall_Street_Journal>\n<http://knowledgebooks.com/schema/aboutCompanyName>\n\"Wall Street Journal\" .\n<https://newsshop.com/june/z902.html>\n<http://knowledgebooks.com/schema/containsCountryDbPediaLink>\n<http://dbpedia.org/resource/Canada> .\nThe following listing of the file src/kgcreator/rdf.lisp generates RDF data:\n1\n(in-package #:kgcreator)\n2\n3\n(let ((*rdf-nodes-hash*))\n4\n5\n(defun rdf-from-files (output-file-path text-and-meta-pairs)\n6\n(setf *rdf-nodes-hash* (make-hash-table :test #'equal :size 200))\n7\n(print (list \"==> rdf-from-files\" output-file-path text-and-meta-pairs ))\n8\n(with-open-file\n9\n(str output-file-path\n10\n:direction :output\n11\n:if-exists :supersede\n12\n:if-does-not-exist :create)\n13\n14\n(defun rdf-from-files-handle-single-file (text-input-file meta-input-file)\n15\n(let* ((text (file-to-string text-input-file))\n16\n(words (myutils:words-from-string text))\n17\n(meta (file-to-string meta-input-file)))\n18\n19\n(defun generate-original-doc-node-rdf ()\n20\n(let ((node-name (node-name-from-uri meta)))\n21\n(if (null (gethash node-name *rdf-nodes-hash*))\n22\n(let* ((cats (categorize words))\n23\n(sum (summarize words cats)))\n24\n(print (list \"$$$$$$\ncats:\" cats))\n25\n(setf (gethash node-name *rdf-nodes-hash*) t)\n26\n(format str (concatenate 'string \"<\" meta\n27\n\"> <http:knowledgebooks.com/schema/summary> \\\"\"\n28\nsum \"\\\" . ~%\"))\n29\n(dolist (cat cats)\n30\n(let ((hash-check (concatenate 'string node-name (car cat))))\n\nAutomatically Generating Data for Knowledge Graphs\n194\n31\n(if (null (gethash hash-check *rdf-nodes-hash*))\n32\n(let ()\n33\n(setf (gethash hash-check *rdf-nodes-hash*) t)\n34\n(format str\n35\n(concatenate 'string \"<\" meta\n36\n\"> <http://knowledgebooks.com/schema/\"\n37\n\"topicCategory> \"\n38\n\"<http://knowledgebooks.com/schema/\"\n39\n(car cat) \"> . ~%\"))))))))))\n40\n41\n(defun generate-dbpedia-contains-rdf (key value)\n42\n(generate-original-doc-node-rdf)\n43\n(let ((relation-name (concatenate 'string key \"DbPediaLink\")))\n44\n(dolist (entity-pair value)\n45\n(let* ((node-name (node-name-from-uri meta))\n46\n(object-node-name (node-name-from-uri (cadr entity-pair)))\n47\n(hash-check (concatenate 'string node-name object-node-name)))\n48\n(if (null (gethash hash-check *rdf-nodes-hash*))\n49\n(let ()\n50\n(setf (gethash hash-check *rdf-nodes-hash*) t)\n51\n(format str (concatenate 'string \"<\" meta\n52\n\"> <http://knowledgebooks.com/schema/contains/\"\n53\nkey \"> \" (cadr entity-pair) \" .~%\"))))))))))\n54\n55\n56\n;; start code for rdf-from-files (output-file-path text-and-meta-pairs)\n57\n(dolist (pair text-and-meta-pairs)\n58\n(rdf-from-files-handle-single-file (car pair) (cadr pair))\n59\n(let ((h (entities_dbpedia:find-entities-in-text (file-to-string (car pair))\\\n60\n)))\n61\n(entities_dbpedia:entity-iterator #'generate-dbpedia-contains-rdf h))))))\n62\n63\n64\n(defvar test_files '((#P\"~/GITHUB/common-lisp/kgcreator/test_data/test3.txt\"\n65\n#P\"~/GITHUB/common-lisp/kgcreator/test_data/test3.meta\")))\n66\n(defvar test_filesZZZ '((#P\"~/GITHUB/common-lisp/kgcreator/test_data/test3.txt\"\n67\n#P\"~/GITHUB/common-lisp/kgcreator/test_data/test3.meta\")\n68\n(#P\"~/GITHUB/common-lisp/kgcreator/test_data/test2.txt\"\n69\n#P\"~/GITHUB/common-lisp/kgcreator/test_data/test2.meta\")\n70\n(#P\"~/GITHUB/common-lisp/kgcreator/test_data/test1.txt\"\n71\n#P\"~/GITHUB/common-lisp/kgcreator/test_data/test1.meta\")))\n72\n73\n(defun test3a ()\n\nAutomatically Generating Data for Knowledge Graphs\n195\n74\n(rdf-from-files \"out.rdf\" test_files))\nYou can load all of KGCreator but just execute the test function at the end of this file using:\n(ql:quickload \"kgcreator\")\n(in-package #:kgcreator)\n(kgcreator:test3a)\nThis code works on a list of paired files for text data and the meta data for each text file. As an\nexample, if there is an input text file test123.txt then there would be a matching meta file test123.meta\nthat contains the source of the data in the file test123.txt. This data source will be a URI on the web\nor a local file URI. The top level function rdf-from-files takes an output file path for writing the\ngenerated RDF data and a list of pairs of text and meta file paths.\nA global variable *rdf-nodes-hash* will be used to remember the nodes in the RDF graph as it is\ngenerated. Please note that the function rdf-from-files is not re-entrant: it uses the global *rdf-\nnodes-hash* so if you are writing multi-threaded applications it will not work to execute the\nfunction rdf-from-files simultaneously in multiple threads of execution.\nThe function rdf-from-files (and the nested functions) are straightforward. I left a few debug\nprintout statements in the code and when you run the test code that I left in the bottom of the\nfile, hopefully it will be clear what rdf.lisp is doing.\nGenerating Data for the Neo4j Graph Database\nNow we will generate Neo4J Cypher data. In order to keep the implementation simple, both the RDF\nand Cypher generation code starts with raw text and performs the NLP analysis to find entities. This\nexample could be refactored to perform the NLP analysis just one time but in practice you will likely\nbe working with either RDF or NEO4J and so you will probably extract just the code you need from\nthis example (i.e., either the RDF or Cypher generation code).\nBefore we look at the code, let’s start with a few lines of generated Neo4J Cypher import data:\nCREATE (newsshop_com_june_z902_html_news)-[:ContainsCompanyDbPediaLink]->(Wall_Stree\\\nt_Journal)\nCREATE (Canada:Entity {name:\"Canada\", uri:\"<http://dbpedia.org/resource/Canada>\"})\nCREATE (newsshop_com_june_z902_html_news)-[:ContainsCountryDbPediaLink]->(Canada)\nCREATE (summary_of_abcnews_go_com_US_violent_long_lasting_tornadoes_threaten_oklahom\\\na_texas_storyid63146361:Summary {name:\"summary_of_abcnews_go_com_US_violent_long_las\\\nting_tornadoes_threaten_oklahoma_texas_storyid63146361\", uri:\"<https://abcnews.go.co\\\nm/US/violent-long-lasting-tornadoes-threaten-oklahoma-texas/story?id=63146361>\", sum\\\nmary:\"Part of the system that delivered severe weather to the central U.S. over the \\\nweekend is moving into the Northeast today, producing strong to severe storms -- dam\\\n\nAutomatically Generating Data for Knowledge Graphs\n196\naging winds, hail or isolated tornadoes can't be ruled out. Severe weather is foreca\\\nst to continue on Tuesday, with the western storm moving east into the Midwest and p\\\narts of the mid-Mississippi Valley.\"})\nThe following listing of file src/kgcreator/neo4j.lisp is similar to the code that generated RDF in\nthe last section:\n1\n(in-package #:kgcreator)\n2\n3\n(let ((*entity-nodes-hash*))\n4\n5\n(defun cypher-from-files (output-file-path text-and-meta-pairs)\n6\n(setf *entity-nodes-hash* (make-hash-table :test #'equal :size 200))\n7\n;;(print (list \"==> cypher-from-files\"output-file-path text-and-meta-pairs ))\n8\n(with-open-file\n9\n(str output-file-path\n10\n:direction :output\n11\n:if-exists :supersede\n12\n:if-does-not-exist :create)\n13\n14\n(defun generateNeo4jCategoryNodes ()\n15\n(let* ((names categorize_summarize::categoryNames))\n16\n(dolist (name names)\n17\n(format str\n18\n(myutils:replace-all\n19\n(concatenate\n20\n'string \"CREATE (\" name \":CategoryType {name:\\\"\" name \"\\\"})~%\")\n21\n\"/\" \"_\"))))\n22\n(format str \"~%\"))\n23\n24\n25\n(defun cypher-from-files-handle-single-file (text-input-file meta-input-file)\n26\n(let* ((text (file-to-string text-input-file))\n27\n(words (myutils:words-from-string text))\n28\n(meta (file-to-string meta-input-file)))\n29\n30\n(defun generate-original-doc-node ()\n31\n(let ((node-name (node-name-from-uri meta)))\n32\n(if (null (gethash node-name *entity-nodes-hash*))\n33\n(let* ((cats (categorize words))\n34\n(sum (summarize words cats)))\n35\n(setf (gethash node-name *entity-nodes-hash*) t)\n36\n(format str (concatenate 'string \"CREATE (\" node-name \":News {name:\\\"\"\n\nAutomatically Generating Data for Knowledge Graphs\n197\n37\nnode-name \"\\\", uri: \\\"\" meta\n38\n\"\\\", summary: \\\"\" sum \"\\\"})~%\"))\n39\n(dolist (cat cats)\n40\n(let ((hash-check (concatenate 'string node-name (car cat))))\n41\n(if (null (gethash hash-check *entity-nodes-hash*))\n42\n(let ()\n43\n(setf (gethash hash-check *entity-nodes-hash*) t)\n44\n(format str (concatenate 'string \"CREATE (\" node-name\n45\n\")-[:Category]->(\"\n46\n(car cat) \")~%\"))))))))))\n47\n48\n(defun generate-dbpedia-nodes (key entity-pairs)\n49\n(dolist (entity-pair entity-pairs)\n50\n(if (null (gethash (node-name-from-uri (cadr entity-pair))\n51\n*entity-nodes-hash*))\n52\n(let ()\n53\n(setf (gethash (node-name-from-uri (cadr entity-pair)) *entity-nodes-hash*) t)\n54\n(format str\n55\n(concatenate 'string \"CREATE (\" (node-name-from-uri (cadr entity-pair))\n56\nkey \" {name: \\\"\" (car entity-pair)\n57\n\"\\\", uri: \\\"\" (cadr entity-pair) \"\\\"})~%\"))))))\n58\n59\n(defun generate-dbpedia-contains-cypher (key value)\n60\n(generate-original-doc-node)\n61\n(generate-dbpedia-nodes key value)\n62\n(let ((relation-name (concatenate 'string key \"DbPediaLink\")))\n63\n(dolist (entity-pair value)\n64\n(let* ((node-name (node-name-from-uri meta))\n65\n(object-node-name (node-name-from-uri (cadr entity-pair)))\n66\n(hash-check (concatenate 'string node-name object-node-name)))\n67\n(if (null (gethash hash-check *entity-nodes-hash*))\n68\n(let ()\n69\n(setf (gethash hash-check *entity-nodes-hash*) t)\n70\n(format str (concatenate 'string\n71\n\"CREATE (\" node-name \")-[:\"\n72\nrelation-name \"]->(\" object-node-name \")~%\"))))))))\n73\n74\n75\n;; start code for cypher-from-files (output-file-path text-and-meta-pairs)\n76\n(generateNeo4jCategoryNodes) ;; just once, not for every input file\n77\n(dolist (pair text-and-meta-pairs)\n78\n(cypher-from-files-handle-single-file (car pair) (cadr pair))\n79\n(let ((h (entities_dbpedia:find-entities-in-text (file-to-string (car pair)))))\n\nAutomatically Generating Data for Knowledge Graphs\n198\n80\n(entities_dbpedia:entity-iterator #'generate-dbpedia-contains-cypher h))))))\n81\n82\n83\n(defvar test_files '((#P\"~/GITHUB/common-lisp/kgcreator/test_data/test3.txt\"\n84\n#P\"~/GITHUB/common-lisp/kgcreator/test_data/test3.meta\")\n85\n(#P\"~/GITHUB/common-lisp/kgcreator/test_data/test2.txt\"\n86\n#P\"~/GITHUB/common-lisp/kgcreator/test_data/test2.meta\")\n87\n(#P\"~/GITHUB/common-lisp/kgcreator/test_data/test1.txt\"\n88\n#P\"~/GITHUB/common-lisp/kgcreator/test_data/test1.meta\")))\n89\n90\n(defun test2a ()\n91\n(cypher-from-files \"out.cypher\" test_files))\nYou can load all of KGCreator but just execute the test function at the end of this file using:\n(ql:quickload \"kgcreator\")\n(in-package #:kgcreator)\n(kgcreator:test2a)\nImplementing the Top Level Application APIs\nThe code in the file src/kgcreator/kgcreator.lisp uses both rdf.lisp and neo4j.lisp that we saw in\nthe last two sections. The function get-files-and-meta looks at the contents of an input directory\nto generate a list of pairs, each pair containing the path to a text file and the meta file for the\ncorresponding text file.\nWe are using the opts package to parse command line arguments. This will be used when we\nbuild a single file standalone executable file for the entire KGCreator application, including the\nweb application that we will see in a later section.\n1\n;; KGCreator main program\n2\n3\n(in-package #:kgcreator)\n4\n5\n(ensure-directories-exist \"temp/\")\n6\n7\n(defun get-files-and-meta (fpath)\n8\n(let ((data (directory (concatenate 'string fpath \"/\" \"*.txt\")))\n9\n(meta (directory (concatenate 'string fpath \"/\" \"*.meta\"))))\n10\n(if (not (equal (length data) (length meta)))\n11\n(let ()\n\nAutomatically Generating Data for Knowledge Graphs\n199\n12\n(princ \"Error: must be matching *.meta files for each *.txt file\")\n13\n(terpri)\n14\n'())\n15\n(let ((ret '()))\n16\n(dotimes (i (length data))\n17\n(setq ret (cons (list (nth i data) (nth i meta)) ret)))\n18\nret))))\n19\n20\n(opts:define-opts\n21\n(:name :help\n22\n:description\n23\n\"KGcreator command line example: ./KGcreator -i test_data -r out.rdf -c out.cyp\\\n24\ner\"\n25\n:short #\\h\n26\n:long \"help\")\n27\n(:name :rdf\n28\n:description \"RDF output file name\"\n29\n:short #\\r\n30\n:long \"rdf\"\n31\n:arg-parser #'identity ;; <- takes an argument\n32\n:arg-parser #'identity) ;; <- takes an argument\n33\n(:name :cypher\n34\n:description \"Cypher output file name\"\n35\n:short #\\c\n36\n:long \"cypher\"\n37\n:arg-parser #'identity) ;; <- takes an argument\n38\n(:name :inputdir\n39\n:description \"Cypher output file name\"\n40\n:short #\\i\n41\n:long \"inputdir\"\n42\n:arg-parser #'identity)) ;; <- takes an argument\n43\n44\n45\n(defun kgcreator () ;; don't need: &aux args sb-ext:*posix-argv*)\n46\n(handler-case\n47\n(let* ((opts (opts:get-opts))\n48\n(input-path\n49\n(if (find :inputdir opts)\n50\n(nth (1+ (position :inputdir opts)) opts)))\n51\n(rdf-output-path\n52\n(if (find :rdf opts)\n53\n(nth (1+ (position :rdf opts)) opts)))\n54\n(cypher-output-path\n\nAutomatically Generating Data for Knowledge Graphs\n200\n55\n(if (find :cypher opts)\n56\n(nth (1+ (position :cypher opts)) opts))))\n57\n(format t \"input-path: ~a\nrdf-output-path: ~a cypher-output-path:~a~%\"\n58\ninput-path rdf-output-path cypher-output-path)\n59\n(if (not input-path)\n60\n(format t \"You must specify an input path.~%\")\n61\n(locally\n62\n(declare #+sbcl(sb-ext:muffle-conditions sb-kernel:redefinition-warning))\n63\n(handler-bind\n64\n(#+sbcl(sb-kernel:redefinition-warning #'muffle-warning))\n65\n;; stuff that emits redefinition-warning's\n66\n(let ()\n67\n(if rdf-output-path\n68\n(rdf-from-files rdf-output-path (get-files-and-meta input-path)))\n69\n(if cypher-output-path\n70\n(cypher-from-files cypher-output-path (get-files-and-meta input-path))))))))\n71\n(t (c)\n72\n(format t \"We caught a runtime error: ~a~%\" c)\n73\n(values 0 c)))\n74\n(format t \"~%Shutting down KGcreator - done processing~%~%\"))\n75\n76\n(defun test1 ()\n77\n(get-files-and-meta\n78\n\"~/GITHUB/common-lisp/kgcreator/test_data\"))\n79\n80\n(defun print-hash-entry (key value)\n81\n(format t \"The value associated with the key ~S is ~S~%\" key value))\n82\n83\n(defun test2 ()\n84\n(let ((h (entities_dbpedia:find-entities-in-text \"Bill Clinton and George Bush wen\\\n85\nt to Mexico and England and watched Univision. They enjoyed Dakbayan sa Dabaw and sh\\\n86\noped at Best Buy and listened to Al Stewart. They agree on RepÃºblica de Nicaragua a\\\n87\nnd support Sweden Democrats and Leicestershire Miners Association and both sent thei\\\n88\nr kids to Darul Uloom Deoband.\")))\n89\n(entities_dbpedia:entity-iterator #'print-hash-entry h)))\n90\n91\n(defun test7 ()\n92\n(rdf-from-files \"out.rdf\" (get-files-and-meta \"test_data\")))\nYou can load all of KGCreator but just execute the three test functions at the end of this file using:\n\nAutomatically Generating Data for Knowledge Graphs\n201\n(ql:quickload \"kgcreator\")\n(in-package #:kgcreator)\n(kgcreator:test1)\n(kgcreator:test2)\n(kgcreator:test7)\nImplementing The Web Interface\nWhen we build a standalone single file application for KGCreator, we include a simple web\napplication interface that allows users to enter input text and see generated RDF and Neo4j Cypher\ndata.\nThe file src/kgcreator/web.lisp uses the libraries cl-who hunchentoot parenscript that we used\nearlier. The function write-files-run-code** (lines 8-43) takes raw text, and writes generated RDF and\nNeo4j Cypher data to local temporary files that are then read and formatted to HTML for display.\nThe code in rdf.lisp and neo4j.lisp is file oriented, and I wrote web.lisp as an afterthought so it was\neasier writing temporary files than refactoring rdf.lisp and neo4j.lisp to write to strings.\n1\n(in-package #:kgcreator)\n2\n3\n(ql:quickload '(cl-who hunchentoot parenscript))\n4\n5\n6\n(setf (html-mode) :html5)\n7\n8\n(defun write-files-run-code (a-uri raw-text)\n9\n(if (< (length raw-text) 10)\n10\n(list \"not enough text\" \"not enough text\")\n11\n;; generate random file number\n12\n(let* ((filenum (+ 1000 (random 5000)))\n13\n(meta-name (concatenate 'string \"temp/\" (write-to-string filenum) \".meta\"))\n14\n(text-name (concatenate 'string \"temp/\" (write-to-string filenum) \".txt\"))\n15\n(rdf-name (concatenate 'string \"temp/\" (write-to-string filenum) \".rdf\"))\n16\n(cypher-name (concatenate 'string \"temp/\" (write-to-string filenum) \".cypher\"))\n17\nret)\n18\n;; write meta file\n19\n(with-open-file (str meta-name\n20\n:direction :output\n21\n:if-exists :supersede\n22\n:if-does-not-exist :create)\n23\n(format str a-uri))\n24\n;; write text file\n\nAutomatically Generating Data for Knowledge Graphs\n202\n25\n(with-open-file (str text-name\n26\n:direction :output\n27\n:if-exists :supersede\n28\n:if-does-not-exist :create)\n29\n(format str raw-text))\n30\n;; generate rdf and cypher files\n31\n(rdf-from-files rdf-name (list (list text-name meta-name)))\n32\n(cypher-from-files cypher-name (list (list text-name meta-name)))\n33\n;; read files and return results\n34\n(setf ret\n35\n(list\n36\n(replace-all\n37\n(replace-all\n38\n(uiop:read-file-string rdf-name)\n39\n\">\" \"&gt;\")\n40\n\"<\" \"&lt;\")\n41\n(uiop:read-file-string cypher-name)))\n42\n(print (list \"ret:\" ret))\n43\nret)))\n44\n45\n(defvar *h* (make-instance 'easy-acceptor :port 3000))\n46\n47\n;; define a handler with the arbitrary name my-greetings:\n48\n49\n(define-easy-handler (my-greetings :uri \"/\") (text)\n50\n(setf (hunchentoot:content-type*) \"text/html\")\n51\n(let ((rdf-and-cypher (write-files-run-code \"http://test.com/1\" text)))\n52\n(print (list \"*** rdf-and-cypher:\" rdf-and-cypher))\n53\n(with-html-output-to-string\n54\n(*standard-output* nil :prologue t)\n55\n(:html\n56\n(:head (:title \"KGCreator Demo\")\n57\n(:link :rel \"stylesheet\" :href \"styles.css\" :type \"text/css\"))\n58\n(:body\n59\n:style \"margin: 90px\"\n60\n(:h1 \"Enter plain text for the demo to create RDF and Cypher\")\n61\n(:p \"For more information on the KGCreator product please visit the web site:\"\n62\n(:a :href \"https://markwatson.com/products/\" \"Mark Watson's commercial products\"\\\n63\n))\n64\n(:p \"The KGCreator product is a command line tool that processes all text \"\n65\n\"web applications and files in a source directory and produces both RDF data \"\n66\n\"triples for semantic Cypher input data files for the Neo4j graph database. \"\n67\n\"For the purposes of this demo the URI for your input text is hardwired to \"\n\nAutomatically Generating Data for Knowledge Graphs\n203\n68\n\"&lt;http://test.com/1&gt; but the KGCreator product offers flexibility \"\n69\n\"for assigning URIs to data sources and further, \"\n70\n\"creates links for relationships between input sources.\")\n71\n(:p :style \"text-align: left\"\n72\n\"To try the demo paste plain text into the following form that contains \"\n73\n\"information on companies, news, politics, famous people, broadcasting \"\n74\n\"networks, political parties, countries and other locations, etc. \")\n75\n(:p \"Do not include and special characters or character sets:\")\n76\n(:form\n77\n:method :post\n78\n(:textarea\n79\n:rows \"20\"\n80\n:cols \"90\"\n81\n:name \"text\"\n82\n:value text)\n83\n(:br)\n84\n(:input :type :submit :value \"Submit text to process\"))\n85\n(:h3 \"RDF:\")\n86\n(:pre (str (car rdf-and-cypher)))\n87\n(:h3 \"Cypher:\")\n88\n(:pre (str (cadr rdf-and-cypher))))))))\n89\n90\n(defun kgcweb ()\n91\n(hunchentoot:start *h*))\nYou can load all of KGCreator and start the web application using:\n(ql:quickload \"kgcreator\")\n(in-package #:kgcreator)\n(kgcweb)\nYou can access the web app at http://localhost:3000⁸⁵.\nCreating a Standalone Application Using SBCL\nWhen I originally wrote KGCreator I intended to develop a commercial product so it was important\nto be able to create standalone single file executables. This is simple to do using SBCL:\n⁸⁵http://localhost:3000\n\nAutomatically Generating Data for Knowledge Graphs\n204\n1\n$ sbcl\n2\n(ql:quickload \"kgcreator\")\n3\n(in-package #:kgcreator)\n4\n(sb-ext:save-lisp-and-die \"KGcreator\"\n5\n:toplevel #'kgcreator :executable t)\nAs an example, you could run the application on the command line using:\n1\n./KGcreator -i test_data -r out.rdf -c out.cyper\nAugmenting RDF Triples in a Knowledge Graph Using\nDBPedia\nYou can augment RDF-based Knowledge Graphs that you build with the KGcreator application by\nusing the library in the directory kg-add-dbpedia-triples.\nAs seen in the kg-add-dbpedia-triples.asd and package.lisp configuration files, we use two other\nlibraries developed in this book:\n;;;; kg-add-dbpedia-triples.asd\n(asdf:defsystem #:kg-add-dbpedia-triples\n:description \"Add DBPedia triples from an input N-Triples RDF file\"\n:author \"markw@markwatson.com\"\n:license \"Apache 2\"\n:depends-on (#:myutils #:sparql)\n:components ((:file \"package\")\n(:file \"add-dbpedia-triples\")))\n;;;; package.lisp\n(defpackage #:kg-add-dbpedia-triples\n(:use #:cl #:myutils #:sparql)\n(:export #:add-triples))\nThe library is implemented in the file kg-add-dbpedia-triples.lisp:\n\nAutomatically Generating Data for Knowledge Graphs\n205\n1\n(in-package #:kg-add-dbpedia-triples)\n2\n3\n(defun augmented-triples (a-uri ostream)\n4\n(let ((results\n5\n(sparql:dbpedia\n6\n(format nil \"construct { ~A ?p ?o } where { ~A ?p ?o } limit 5\" a-uri a-ur\\\n7\ni))))\n8\n(dolist (x results)\n9\n(dolist (sop x)\n10\n(let ((val (second sop)))\n11\n(if (and\n12\n(stringp val)\n13\n(> (length val) 9)\n14\n(or\n15\n(equal (subseq val 0 7) \"http://\")\n16\n(equal (subseq val 0 8) \"https://\")))\n17\n(format ostream \"<~A> \" val)\n18\n(format ostream \"~A \" val))))\n19\n(format ostream \" .~%\"))))\n20\n21\n(defun add-triples (in-file-name out-file-name)\n22\n(let* ((nt-data (myutils:file-to-string in-file-name))\n23\n(tokens (myutils:tokenize-string-keep-uri nt-data))\n24\n(uris\n25\n(remove-duplicates\n26\n(mapcan #'(lambda (s) (if\n27\n(and\n28\n(stringp s)\n29\n(> (length s) 19)\n30\n(equal (subseq s 0 19) \"<http://dbpedia.org\"))\n31\n(list s)))\n32\ntokens)\n33\n:test #'equal)))\n34\n(with-open-file (str out-file-name\n35\n:direction :output\n36\n:if-exists :supersede\n37\n:if-does-not-exist :create)\n38\n(dolist (uri uris)\n39\n(augmented-triples uri str)))))\nTBD\n\nAutomatically Generating Data for Knowledge Graphs\n206\n1\nKGCreator Wrap Up\nWhen developing applications or systems using Knowledge Graphs it is useful to be able to quickly\ngenerate test data which is the primary purpose of KGCreator. A secondary use is to generate\nKnowledge Graphs for production use using text data sources. In this second use case you will want\nto manually inspect the generated data to verify its correctness or usefulness for your application.\n\nKnowledge Graph Sampler for\nCreating Small Custom Knowledge\nGraphs\nI find it convenient to be able to “sample” small parts of larger knowledge graphs. The example\nprogram in this chapter accepts a list of DBPedia entity URIs, attempts top find links between these\nentities, and writes these nodes and discovered edges to a RDF triples file.\nThe code is in the directory src/kgsampler. As seen in the configuration files kg-add-dbpedia-\ntriples.asd and package.lisp, we will use the sparql library we developed earlier as well as the\nlibraries uiop and drakma:\n;;;; kgsampler.asd\n(asdf:defsystem #:kgsampler\n:description \"sample knowledge graphs\"\n:author \"Mark Watson markw@markwatson.com\"\n:license \"Apache 2\"\n:depends-on (#:uiop #:drakma #:sparql)\n:components ((:file \"package\")\n(:file \"kgsampler\")))\n;;;; package.lisp\n(defpackage #:kgsampler\n(:use #:cl #:uiop #:sparql)\n(:export #:sample))\nThe program starts with a list of entities and tries to find links on DBPedia between the entities.\nA small sample graph of the input entities and any discovered links is written to a file. The\nfunction dbpedia-as-nt spawns a process to use the curl utility to make a HTTP request to DBPedia.\nThe function construct-from-dbpedia takes a list of entities and writes SPARQL CONSTRUCT\nstatements with the entity as the subject and the object filtered to a string value in the English\nlanguage to an output stream. The function find-relations runs at O(N^2) where N is the number\nof input entities so you should avoid using this program with a large number of input entities.\nI offer this code with little explanation since much of it is similar to the techniques you saw in the\nprevious chapter Knowledge Graph Navigator.\n\nKnowledge Graph Sampler for Creating Small Custom Knowledge Graphs\n208\n;; kgsampler main program\n(in-package #:kgsampler)\n(defun dbpedia-as-nt (query)\n(print query)\n(uiop:run-program\n(list\n\"curl\"\n(concatenate 'string\n\"https://dbpedia.org/sparql?format=text/ntriples&query=\"\n;; formats that work: csv, text/ntriples, text/ttl\n(drakma:url-encode query :utf-8)))\n:output :string))\n(defun construct-from-dbpedia (entity-uri-list &key (output-stream t))\n(dolist (entity-uri entity-uri-list)\n(format output-stream \"~%~%# ENTITY NAME: ~A~%~%\" entity-uri)\n(format\noutput-stream\n(dbpedia-as-nt\n(format nil\n\"CONSTRUCT { ~A ?p ?o } where { ~A ?p ?o\n. FILTER(lang(?o) = 'en') }\"\\\nentity-uri entity-uri)))))\n(defun ensure-angle-brackets (s)\n\"make sure URIs have angle brackets\"\n(if (equal #\\< (char s 0))\ns\n(concatenate 'string \"<\" s \">\")))\n(defun find-relations (entity-uri-list &key (output-stream t))\n(dolist (entity-uri1 entity-uri-list)\n(dolist (entity-uri2 entity-uri-list)\n(if (not (equal entity-uri1 entity-uri2))\n(let ((possible-relations\n(mapcar #'cadar\n(sparql::dbpedia\n(format nil\n\"select ?p where { ~A ?p ~A . filter(!regex(str(?p\\\n), \\\"page\\\", \\\"i\\\"))} limit 50\"\nentity-uri1 entity-uri2)))))\n\nKnowledge Graph Sampler for Creating Small Custom Knowledge Graphs\n209\n(print \"** possible-relations:\") (print possible-relations)\n(dolist (pr possible-relations)\n(format output-stream \"~A ~A ~a .~%\"\nentity-uri1\n(ensure-angle-brackets pr)\nentity-uri2)))))))\n(defun sample (entity-uri-list output-filepath)\n(with-open-file (ostream\n(pathname output-filepath) :direction :output :if-exists\\\n:supersede)\n(construct-from-dbpedia entity-uri-list :output-stream ostream)\n(find-relations entity-uri-list :output-stream ostream)))\nLet’s start by running the two helper functions interactively so you can see their output (output\nedited for brevity). The top level function kgsampler:sample for this example takes a list of entity\nURIs and an output file name, and uses the functions construct-from-dbpedia entity-uri-list and\nfind-relations to write triples for the entities and then for the relationships discovered between\nentities. The following listing also calls the helper function kgsampler::find-relations to show you\nwhat its output looks like.\n$ sbcl\n* (ql:quickload \"kgsampler\")\n*\n(kgsampler::construct-from-dbpedia '(\"<http://dbpedia.org/resource/Bill_Gates>\" \\\n\"<http://dbpedia.org/resource/Steve_Jobs>\") :output-stream nil)\n\"CONSTRUCT { <http://dbpedia.org/resource/Bill_Gates> ?p ?o } where { <http://dbpedi\\\na.org/resource/Bill_Gates> ?p ?o\n. FILTER (lang(?o) = 'en') }\"\n\"CONSTRUCT { <http://dbpedia.org/resource/Bill_Gates> <http://purl.org/dc/terms/subj\\\nect> ?o } where { <http://dbpedia.org/resource/Bill_Gates> <http://purl.org/dc/terms\\\n/subject> ?o\n}\"\n...\n* (kgsampler::find-relations '(\"<http://dbpedia.org/resource/Bill_Gates>\" \"<http://d\\\nbpedia.org/resource/Microsoft>\") :output-stream nil)\n(\"dbpedia SPARQL:\"\n\"select ?p where { <http://dbpedia.org/resource/Bill_Gates> ?p <http://dbpedia.org/\\\nresource/Microsoft> . filter(!regex(str(?p), \\\"page\\\", \\\"i\\\"))} limit 50\"\n\"n\")\n\"** possible-relations:\"\n(\"http://dbpedia.org/ontology/knownFor\")\n\"http://dbpedia.org/ontology/knownFor\"\n\nKnowledge Graph Sampler for Creating Small Custom Knowledge Graphs\n210\n(\"dbpedia SPARQL:\"\n\"select ?p where { <http://dbpedia.org/resource/Microsoft> ?p <http://dbpedia.org/r\\\nesource/Bill_Gates> . filter(!regex(str(?p), \\\"page\\\", \\\"i\\\"))} limit 50\"\n\"n\")\n\"** possible-relations:\"\n(\"http://dbpedia.org/property/founders\" \"http://dbpedia.org/ontology/foundedBy\")\n\"http://dbpedia.org/property/founders\"\n\"http://dbpedia.org/ontology/foundedBy\"\nnil\nWe now use the main function to generate an output RDF triple file:\n1\n$ sbcl\n2\n* (ql:quickload \"kgsampler\")\n3\n* (kgsampler:sample '(\"<http://dbpedia.org/resource/Bill_Gates>\" \"<http://dbpedia.or\\\n4\ng/resource/Steve_Jobs>\" \"<http://dbpedia.org/resource/Microsoft>\")\n\"test.nt\")\n5\n\"CONSTRUCT { <http://dbpedia.org/resource/Bill_Gates> ?p ?o } where { <http://dbpedi\\\n6\na.org/resource/Bill_Gates> ?p ?o\n. FILTER (lang(?o) = 'en') }\"\n7\n(\"ndbpedia SPARQL:n\"\n8\n\"select ?p where { <http://dbpedia.org/resource/Bill_Gates> ?p <http://dbpedia.org/\\\n9\nresource/Microsoft> . filter(!regex(str(?p), \\\"page\\\", \\\"i\\\"))} limit 50\"\n10\n\"n\")\n11\n\"** possible-relations:\"\n12\n(\"http://dbpedia.org/ontology/board\")\n13\n(\"dbpedia SPARQL:\"\n14\n\"select ?p where { <http://dbpedia.org/resource/Steve_Jobs> ?p <http://dbpedia.org/\\\n15\nresource/Bill_Gates> . filter(!regex(str(?p), \\\"page\\\", \\\"i\\\"))} limit 50\"\n16\n\"n\")\nOutput RDF N-Triple data is written to the file sample-KG.nt. A very small part of this file is listed\nhere:\n1\n# ENTITY NAME: <http://dbpedia.org/resource/Bill_Gates>\n2\n3\n<http://dbpedia.org/resource/Bill_Gates>\n<http://dbpedia.org/ontology/abstrac\\\n4\nt>\n\"William Henry \\\"Bill\\\" Gates III (born October 28, 1955) is an American busines\\\n5\ns magnate,....\"@en .\n6\n<http://dbpedia.org/resource/Bill_Gates>\n7\n<http://xmlns.com/foaf/0.1/name>\n8\n\"Bill Gates\"@en .\n9\n<http://dbpedia.org/resource/Bill_Gates>\n10\n<http://xmlns.com/foaf/0.1/surname>\n\nKnowledge Graph Sampler for Creating Small Custom Knowledge Graphs\n211\n11\n\"Gates\"@en .\n12\n<http://dbpedia.org/resource/Bill_Gates>\n13\n<http://dbpedia.org/ontology/title>\n14\n\"Co-Chairmanof theBill & Melinda Gates Foundation\"@en .\nThe same data in Turtle RDF format can be seen in the file sample-KG.ttl that was produced by\nimporting the triples file into the free edition of GraphDB exporting it to the Turtle file sample-\nKG.ttl that I find easier to read. GraphDB has visualization tools which I use here to generate an\ninteractive graph display of this data:\nGraphDB Visual graph of generated RDF triples\nThis example is also set up for people and companies. I may expand it in the future to other types\nof entities as I need them.\nThis example program takes several minutes to run since many SPARQL queries are made to\nDBPedia. I am a non-corporate member of the DBPedia organization. Here is a membership\napplication⁸⁶ if you are interested in joining me there.\n⁸⁶https://www.dbpedia.org/members/membership/\n\nKnowledge Graph Navigator Common\nLibrary Implementation\nThe Knowledge Graph Navigator (which I will often refer to as KGN) is a tool for processing a set\nof entity names and automatically exploring the public Knowledge Graph DBPedia⁸⁷ using SPARQL\nqueries. I started to write KGN for my own use, to automate some things I used to do manually\nwhen exploring Knowledge Graphs, and later thought that KGN might be also useful for educational\npurposes. KGN shows the user the auto-generated SPARQL queries so hopefully the user will learn\nby seeing examples. KGN uses NLP code developed in earlier chapters and we will reuse that code\nwith a short review of using the APIs.\nIn previous versions of this book, this example was hard-wired to use LispWork CAPI for the\nuser interface. This old version is in src/kgn in the main GitHub repository for this book:\nhttps://github.com/mark-watson/loving-common-lisp⁸⁸ and has a few UI components like a progress\nbar that I removed since the previous edition. The new version has separate GitHub repositories for:\n• https://github.com/mark-watson/kgn-common⁸⁹ for the Knowledge Graph Navigator common\nlibrary.\n• https://github.com/mark-watson/kgn-text-ui⁹⁰ for a text interface for the Knowledge Graph\nNavigator.\n• https://github.com/mark-watson/kgn-capi-ui⁹¹ for a LispWorks CAPI GUI interface for the\nKnowledge Graph Navigator.\nIf you followed the code example setup instructions in the book Preface or in the README file in\nthe main repo https://github.com/mark-watson/loving-common-lisp⁹² then all three of these projects\nare available for loading via Quicklisp on your computer.\nAfter looking at SPARQL generated by this example for an example query, we will start a process\nof bottom up development, first writing low level functions to automate SPARQL queries, writing\nutilities we will need for the UIs developed in later chapters.\nSince the DBPedia SPARQL queries are time consuming, we will also implement a caching layer\nusing SQLite that will make the app more responsive. The cache is especially helpful during\ndevelopment when the same queries are repeatedly used for testing.\n⁸⁷http://dbpedia.org\n⁸⁸https://github.com/mark-watson/loving-common-lisp\n⁸⁹https://github.com/mark-watson/kgn-common\n⁹⁰https://github.com/mark-watson/kgn-text-ui\n⁹¹https://github.com/mark-watson/kgn-capi-ui\n⁹²https://github.com/mark-watson/loving-common-lisp\n\nKnowledge Graph Navigator Common Library Implementation\n213\nThe code for this reusable library is in the directory src/kgn-common. This is common library that\nwill be used for user interfaces developed in later chapters. There is a lot of code in the following\nprogram listings and I hope to provide you with a roadmap overview of the code, diving in on code\nthat you might want to reuse for your own projects and some representative code for generating\nSPARQL queries.\nLet’s start by looking at the files for the common library:\n• Makefile - contains development shortcuts.\n• data - data used to remove stop words from text.\n• kgn-common.lisp - main code file for library.\n• package.lisp - standard Common Lisp package definition.\n• utils.lisp - miscelanious utility functions.\n• README.txt\n• kgn-common.asd - standard Common Lisp ASDF definition.\nExample Output\nBefore we get started studying the implementation, let’s look at sample output in order to help give\nmeaning to the code we will look at later. Consider a query that a user might type into the top query\nfield in the KGN app:\n1\nSteve Jobs lived near San Francisco and was\n2\na founder of <http://dbpedia.org/resource/Apple_Inc.>\nThe system will try to recognize entities in a query. If you know the DBPedia URI of an entity, like\nthe company Apple in this example, you can use that directly. Note that in the SPARQL URIs are\nsurrounded with angle bracket characters.\nThe application prints out automatically generated SPARQL queries. For the above listed example\nquery the following output will be generated (some editing to fit page width):\nTrying to get entity by name = Steve Jobs using SPARQL with type:\nselect distinct ?s ?comment { ?s ?p \"Steve Jobs\"@en .\n?s <http://www.w3.org/2000/01/rdf-schema#comment> ?comment .\nFILTER ( lang ( ?comment ) = 'en' ) .\n?s <http://www.w3.org/1999/02/22-rdf-syntax-ns#type>\n<http://dbpedia.org/ontology/Person> .\n} LIMIT 15\n\nKnowledge Graph Navigator Common Library Implementation\n214\nTrying to get entity by name = San Francisco using SPARQL with type:\nselect distinct ?s ?comment { ?s ?p \"San Francisco\"@en .\n?s <http://www.w3.org/2000/01/rdf-schema#comment> ?comment .\nFILTER ( lang ( ?comment ) = 'en' ) .\n?s <http://www.w3.org/1999/02/22-rdf-syntax-ns#type>\n<http://dbpedia.org/ontology/City> .\n} LIMIT 15\nSPARQL to get PERSON data for <http://dbpedia.org/resource/Steve_Jobs>:\nSELECT DISTINCT ?label ?comment\n( GROUP_CONCAT ( DISTINCT ?birthplace ; SEPARATOR=' | ' ) AS ?birthplace )\n( GROUP_CONCAT ( DISTINCT ?almamater ; SEPARATOR=' | ' ) AS ?almamater )\n( GROUP_CONCAT ( DISTINCT ?spouse ; SEPARATOR=' | ' ) AS ?spouse ) {\n<http://dbpedia.org/resource/Steve_Jobs>\n<http://www.w3.org/2000/01/rdf-schema#comment>\n?comment .\nFILTER ( lang ( ?comment ) = 'en' ) .\nOPTIONAL { <http://dbpedia.org/resource/Steve_Jobs>\n<http://dbpedia.org/ontology/birthPlace>\n?birthplace } .\nOPTIONAL { <http://dbpedia.org/resource/Steve_Jobs>\n<http://dbpedia.org/ontology/almaMater>\n?almamater } .\nOPTIONAL { <http://dbpedia.org/resource/Steve_Jobs>\n<http://dbpedia.org/ontology/spouse>\n?spouse } .\nOPTIONAL { <http://dbpedia.org/resource/Steve_Jobs>\n<http://www.w3.org/2000/01/rdf-schema#label>\n?label .\nFILTER ( lang ( ?label ) = 'en' ) }\n} LIMIT 10\nRemember, the SPARQL is generated by KGN from natural language queries. Some more examples:\nSPARQL to get CITY data for <http://dbpedia.org/resource/San_Francisco>:\n\nKnowledge Graph Navigator Common Library Implementation\n215\nSELECT DISTINCT ?label ?comment\n( GROUP_CONCAT ( DISTINCT ?latitude_longitude ; SEPARATOR=' | ' )\nAS ?latitude_longitude )\n( GROUP_CONCAT ( DISTINCT ?populationDensity ; SEPARATOR=' | ' )\nAS ?populationDensity )\n( GROUP_CONCAT ( DISTINCT ?country ; SEPARATOR=' | ' )\nAS ?country ) {\n<http://dbpedia.org/resource/San_Francisco>\n<http://www.w3.org/2000/01/rdf-schema#comment>\n?comment .\nFILTER ( lang ( ?comment ) = 'en' ) .\nOPTIONAL { <http://dbpedia.org/resource/San_Francisco>\n<http://www.w3.org/2003/01/geo/wgs84_pos#geometry>\n?latitude_longitude } .\nOPTIONAL { <http://dbpedia.org/resource/San_Francisco>\n<http://dbpedia.org/ontology/PopulatedPlace/populationDensity>\n?populationDensity } .\nOPTIONAL { <http://dbpedia.org/resource/San_Francisco>\n<http://dbpedia.org/ontology/country>\n?country } .\nOPTIONAL { <http://dbpedia.org/resource/San_Francisco>\n<http://www.w3.org/2000/01/rdf-schema#label>\n?label . }\n} LIMIT 30\nSPARQL to get COMPANY data for <http://dbpedia.org/resource/Apple_Inc.>:\nSELECT DISTINCT ?label ?comment ( GROUP_CONCAT ( DISTINCT ?industry ; SEPARATOR=' | \\\n' )\nAS ?industry )\n( GROUP_CONCAT ( DISTINCT ?netIncome ; SEPARATOR=' | ' )\nAS ?netIncome )\n( GROUP_CONCAT ( DISTINCT ?numberOfEmployees ; SEPARATOR=' | ' )\nAS ?numberOfEmployees ) {\n<http://dbpedia.org/resource/Apple_Inc.>\n<http://www.w3.org/2000/01/rdf-schema#comment> ?comment .\nFILTER ( lang ( ?comment ) = 'en' ) .\nOPTIONAL { <http://dbpedia.org/resource/Apple_Inc.>\n<http://dbpedia.org/ontology/industry>\n?industry } .\nOPTIONAL { <http://dbpedia.org/resource/Apple_Inc.>\n\nKnowledge Graph Navigator Common Library Implementation\n216\n<http://dbpedia.org/ontology/netIncome> ?netIncome } .\nOPTIONAL { <http://dbpedia.org/resource/Apple_Inc.>\n<http://dbpedia.org/ontology/numberOfEmployees> ?numberOfEmployees } .\nOPTIONAL { <http://dbpedia.org/resource/Apple_Inc.>\n<http://www.w3.org/2000/01/rdf-schema#label> ?label .\nFILTER ( lang ( ?label ) = 'en' ) }\n} LIMIT 30\nOnce KGN has identified DBPedia entire URIs, it also searches for relationships between these\nentities:\nDISCOVERED RELATIONSHIP LINKS:\n<http://dbpedia.org/resource/Steve_Jobs>\n->\n<http://dbpedia.org/ontology/birthPlace>\n->\n<http://dbpedia.org/resource/San_Francisco>\n<http://dbpedia.org/resource/Steve_Jobs>\n->\n<http://dbpedia.org/ontology/occupation>\n->\n<http://dbpedia.org/resource/Apple_Inc.>\n<http://dbpedia.org/resource/Steve_Jobs>\n->\n<http://dbpedia.org/ontology/board>\n->\n<http://dbpedia.org/resource/Apple_Inc.>\n<http://dbpedia.org/resource/Steve_Jobs>\n->\n<http://www.w3.org/2000/01/rdf-schema#seeAlso> ->\n<http://dbpedia.org/resource/Apple_Inc.>\n<http://dbpedia.org/resource/Apple_Inc.>\n->\n<http://dbpedia.org/property/founders>\n->\n<http://dbpedia.org/resource/Steve_Jobs>\nAfter listing the generated SPARQL for finding information for the entities in the query, KGN\nsearches for relationships between these entities. These discovered relationships can be seen at\nthe end of the last listing. Please note that this step makes SPARQL queries on O(n^2) where n\nis the number of entities. Local caching of SPARQL queries to DBPedia helps make processing many\nentities possible.\nIn addition to showing generated SPARQL and discovered relationships in the middle text pane of\nthe application, KGN also generates formatted results that are also displayed in the bottom text pane:\n\nKnowledge Graph Navigator Common Library Implementation\n217\n- - - ENTITY TYPE: PEOPLE - - -\nLABEL: Steve Jobs\nCOMMENT: Steven Paul \"Steve\" Jobs was an American information technology\nentrepreneur and inventor. He was the co-founder, chairman, and chief\nexecutive officer (CEO) of Apple Inc.; CEO and majority shareholder\nof Pixar Animation Studios; a member of The Walt Disney Company's\nboard of directors following its acquisition of Pixar; and founder,\nchairman, and CEO of NeXT Inc. Jobs is widely recognized as a pioneer of\nthe microcomputer revolution of the 1970s and 1980s, along with Apple\nco-founder Steve Wozniak. Shortly after his death, Jobs's official\nbiographer, Walter Isaacson, described him as a \"creative entrepreneur\nwhose passion for perfection and ferocious drive revolutionized six industries:\npersonal computers, animated movies, music, phones\nBIRTHPLACE: http://dbpedia.org/resource/San_Francisco\nALMAMATER: http://dbpedia.org/resource/Reed_College\nSPOUSE: http://dbpedia.org/resource/Laurene_Powell_Jobs\n- - - ENTITY TYPE: CITIES - - -\nLABEL:\nSan Francisco\nCOMMENT: San Francisco, officially the City and County of San Francisco, is the\ncultural, commercial, and financial center of Northern California and\nthe only consolidated city-county in California. San Francisco encompasses a\nland area of about 46.9 square miles (121 km2) on the northern end of the\nSan Francisco Peninsula, which makes it the smallest county in the state.\nIt has a density of about 18,451 people per square mile (7,124 people per km2),\nmaking it the most densely settled large city (population greater than\n200,000) in the state of California and the second-most densely populated\nmajor city in the United States after New York City. San Francisco is\nthe fourth-most populous city in California, after Los Angeles, San Diego, and\nSan Jose, and the 13th-most populous cit\nLATITUDE--LONGITUDE: POINT(-122.41666412354 37.783332824707)\nPOPULATION-DENSITY: 7123.97092726667\nCOUNTRY: http://dbpedia.org/resource/United_States\n\nKnowledge Graph Navigator Common Library Implementation\n218\n- - - ENTITY TYPE: COMPANIES - - -\nLABEL: Apple Inc.\nCOMMENT: Apple Inc. is an American multinational technology company headquartered\nin Cupertino,\nCalifornia, that designs, develops, and sells consumer electronics,\ncomputer software, and online services. Its hardware products include the\niPhone smartphone, the iPad tablet computer, the Mac personal computer, the\niPod portable media player, the Apple Watch smartwatch, and the Apple TV digital\nmedia player. Apple's consumer software includes the macOS and iOS operating\nsystems, the iTunes media player, the Safari web browser, and the iLife and\niWork creativity and productivity suites. Its online services include the\niTunes Store, the iOS App Store and Mac App Store, Apple Music, and iCloud.\nINDUSTRY: http://dbpedia.org/resource/Computer_hardware |\nhttp://dbpedia.org/resource/Computer_software |\nhttp://dbpedia.org/resource/Consumer_electronics |\nhttp://dbpedia.org/resource/Corporate_Venture_Capital |\nhttp://dbpedia.org/resource/Digital_distribution |\nhttp://dbpedia.org/resource/Fabless_manufacturing\nNET-INCOME: 5.3394E10\nNUMBER-OF-EMPLOYEES: 115000\nHopefully after reading through sample output and seeing the screen shot of the application, you\nnow have a better idea what this example application does. Now we will look at project configuration\nand then implementation.\nProject Configuration and Running the Application\nThe following listing of kgn.asd shows the ten packages this example depends on (five of these are\nalso examples in this book, and five are in the public Quicklisp repository):\n\nKnowledge Graph Navigator Common Library Implementation\n219\n1\n;;;; knowledgegraphnavigator.asd\n2\n3\n((asdf:defsystem #:kgn-common\n4\n:description \"common utilities for Knowledge Graph Navigator\"\n5\n:author \"Mark Watson <markw@markwatson.com>\"\n6\n:license \"Apache 2\"\n7\n:depends-on (#:sqlite #:cl-json #:alexandria #:drakma #:myutils #:entities #:entit\\\n8\ny-uris #:kbnlp #:sparql-cache)\n9\n:components ((:file \"package\")\n10\n(:file \"utils\")\n11\n(:file \"kgn-common\")))\nListing of package.lisp:\n1\n;;;; package.lisp\n2\n3\n(defpackage #:kgn-common\n4\n(:use #:cl #:alexandria #:myutils\n#:myutils #:sparql-cache\n5\n#:entities #:entity-uris #:kbnlp)\n6\n(:export #:kgn-common #:remove-stop-words\n7\n#:entity-results->relationship-links\n8\n#:get-entity-data-helper #:handle-URIs-in-query\n9\n#:remove-uris-from-query #:get-URIs-in-query #:display-entity-results\n10\n#:string-shorten #:prompt-string #:dbpedia-get-product-detail\n11\n#:dbpedia-get-person-detail #:dbpedia-get-country-detail\n12\n#:dbpedia-get-city-detail #:dbpedia-get-company-detail #:clean-results\n13\n#:dbpedia-get-entities-by-name #:clean-comment))\nWe use ql:quickload to load the KGN common library and call a few APIs (some output removed\nfor brevity):\n1\n$ sbcl\n2\nThis is SBCL 2.1.10, an implementation of ANSI Common Lisp.\n3\n* (ql :kgn-common)\n4\nTo load \"kgn-common\":\n5\nLoad 1 ASDF system:\n6\nkgn-common\n7\n; Loading \"kgn-common\"\n8\n..................\n9\n\"Starting to load data....\"\n10\n\"....done loading data.\"\n11\nTo load \"sqlite\":\n\nKnowledge Graph Navigator Common Library Implementation\n220\n12\nLoad 1 ASDF system:\n13\nsqlite\n14\n; Loading \"sqlite\"\n15\n16\nTo load \"cl-json\":\n17\nLoad 1 ASDF system:\n18\ncl-json\n19\n; Loading \"cl-json\"\n20\n21\nTo load \"drakma\":\n22\nLoad 1 ASDF system:\n23\ndrakma\n24\n; Loading \"drakma\"\n25\n[package kgn-common]..\n26\n(:kgn-common)\n27\n*\n28\n*\n(kgn-common::dbpedia-get-relationships \"<http://dbpedia.org/resource/Bill_Gates>\"\\\n29\n\"<http://dbpedia.org/resource/Microsoft>\")\n30\n(\"<http://dbpedia.org/ontology/knownFor>\")\n31\n*\n32\n*\n(kgn-common:dbpedia-get-entities-by-name \"Bill Gates\" \"<http://dbpedia.org/ontolo\\\n33\ngy/Person>\" \"<http://schema.org/Person>\" :message-stream nil)\n34\n35\n(((:s \"http://dbpedia.org/resource/Bill_Gates\")\n36\n(:comment\n37\n\"William Henry Gates III (born October 28, 1955) is an American business magnate,\\\n38\nsoftware developer, investor, author, and philanthropist. He is a co-founder of Mic\\\n39\nrosoft, along with his late childhood friend Paul Allen. During his career at Micros\\\n40\noft, Gates held the positions of chairman, chief executive officer (CEO), president \\\n41\nand chief software architect, while also being the largest individual shareholder un\\\n42\ntil May 2014. He is considered one of the best known entrepreneurs of the microcompu\\\n43\nter revolution of the 1970s and 1980s.\"))\n44\n((:s \"http://dbpedia.org/resource/Harry_R._Lewis\")\n45\n(:comment\n46\n\"Harry Roy Lewis (born 1947) is an American computer scientist, mathe 00ADma 00AD\\\n47\nti 00ADcian, and uni 00ADver 00ADsity admin 00ADi 00ADstra 00ADtor known for his res\\\n48\nearch in com 00ADpu 00ADta 00ADtional logic, textbooks in theoretical computer scien\\\n49\nce, and writings on computing, higher education, and technology. He is Gordon McKay \\\n50\nProfessor of Computer Science at Harvard University, and was Dean of Harvard College\\\n51\nfrom 1995 to 2003. A new professorship in Engineering and Applied Sciences, endowed\\\n52\nby a former student, will be named for Lewis and his wife upon their retirements.\")\\\n53\n)\n54\n((:s \"http://dbpedia.org/resource/Cascade_Investment\")\n\nKnowledge Graph Navigator Common Library Implementation\n221\n55\n(:comment\n56\n\"Cascade Investment, L.L.C. is an American holding company and private investment\\\n57\nfirm headquartered in Kirkland, Washington, United States. It is controlled by Bill\\\n58\nGates, and managed by Michael Larson. More than half of Gates' fortune is held in a\\\n59\nssets outside his holding of Microsoft shares. Cascade is the successor company to D\\\n60\nominion Income Management, the former investment vehicle for Gates' holdings, which \\\n61\nwas managed by convicted felon Andrew Evans.\"))\n62\n((:s \"http://dbpedia.org/resource/Jerry_Dyer\")\n63\n(:comment\n64\n\"Jerry P. Dyer (born May 3, 1959) is an American politician and former law enforc\\\n65\nement officer. He is the 26th and current mayor of Fresno, California. Previously, h\\\n66\ne served as the chief of the Fresno Police Department.\")))\n67\n68\nselect distinct ?s ?comment { ?s ?p \"Bill Gates\"@en . @@ ?s <http://www.w3.org/2000/\\\n69\n01/rdf-schema#comment>\n?comment\n. FILTER\n(lang(?comment) = 'en') . @@ ?s <http://\\\n70\nwww.w3.org/1999/02/22-rdf-syntax-ns#type> <http://dbpedia.org/ontology/Person> . @@ \\\n71\n} LIMIT 15\n72\n(((:s \"http://dbpedia.org/resource/Bill_Gates\")\n73\n(:comment\n74\n\"William Henry Gates III (born October 28, 1955) is an American business magnate,\\\n75\nsoftware developer, investor, author, and philanthropist. He is a co-founder of Mic\\\n76\nrosoft, along with his late childhood friend Paul Allen. During his career at Micros\\\n77\noft, Gates held the positions of chairman, chief executive officer (CEO), president \\\n78\nand chief software architect, while also being the largest individual shareholder un\\\n79\ntil May 2014. He is considered one of the best known entrepreneurs of the microcompu\\\n80\nter revolution of the 1970s and 1980s.\"))\n81\n((:s \"http://dbpedia.org/resource/Harry_R._Lewis\")\n82\n(:comment\n83\n\"Harry Roy Lewis (born 1947) is an American computer scientist, mathe 00ADma 00AD\\\n84\nti 00ADcian, and uni 00ADver 00ADsity admin 00ADi 00ADstra 00ADtor known for his res\\\n85\nearch in com 00ADpu 00ADta 00ADtional logic, textbooks in theoretical computer scien\\\n86\nce, and writings on computing, higher education, and technology. He is Gordon McKay \\\n87\nProfessor of Computer Science at Harvard University, and was Dean of Harvard College\\\n88\nfrom 1995 to 2003. A new professorship in Engineering and Applied Sciences, endowed\\\n89\nby a former student, will be named for Lewis and his wife upon their retirements.\")\\\n90\n)\n91\n((:s \"http://dbpedia.org/resource/Cascade_Investment\")\n92\n(:comment\n93\n\"Cascade Investment, L.L.C. is an American holding company and private investment\\\n94\nfirm headquartered in Kirkland, Washington, United States. It is controlled by Bill\\\n95\nGates, and managed by Michael Larson. More than half of Gates' fortune is held in a\\\n96\nssets outside his holding of Microsoft shares. Cascade is the successor company to D\\\n97\nominion Income Management, the former investment vehicle for Gates' holdings, which \\\n\nKnowledge Graph Navigator Common Library Implementation\n222\n98\nwas managed by convicted felon Andrew Evans.\"))\n99\n((:s \"http://dbpedia.org/resource/Jerry_Dyer\")\n100\n(:comment\n101\n\"Jerry P. Dyer (born May 3, 1959) is an American politician and former law enforc\\\n102\nement officer. He is the 26th and current mayor of Fresno, California. Previously, h\\\n103\ne served as the chief of the Fresno Police Department.\")))\n104\n*\nIn this last example, using :message-stream nil effectively turns off printing generated SPARQL\nqueries used by these APIs. You can use :message-stream t to see generated SPARQL.\nEvery time the KGN common library makes a web service call to DBPedia the query and response are\ncached in a SQLite database in ∼/.kgn_cache.db which can greatly speed up the program, especially\nin development mode when testing a set of queries. This caching also takes some load off of the public\nDBPedia endpoint, which is a polite thing to do.\nReview of NLP Utilities Used in Application\nHere is a quick review of NLP utilities we saw in an earlier chapter:\n• kbnlp:make-text-object\n• kbnlp::text-human-names\n• kbnlp::text-place-name\n• entity-uris:find-entities-in-text\n• entity-uris:pp-entities\nThe following code snippets show example calls to the relevant NLP functions and the generated\noutput:\nKGN 39 > (setf text \"Bill Clinton went to Canada\")\n\"Bill Clinton went to Canada\"\nKGN 40 > (setf txtobj (kbnlp:make-text-object text))\n#S(TEXT :URL \"\" :TITLE \"\" :SUMMARY \"<no summary>\" :CATEGORY-TAGS ((\"computers_micros\\\noft.txt\" 0.00641) (\"religion_islam.txt\" 0.00357)) :KEY-WORDS NIL :KEY-PHRASES NIL :H\\\nUMAN-NAMES (\"Bill Clinton\") :PLACE-NAMES (\"Canada\") :COMPANY-NAMES NIL :TEXT #(\"Bill\\\n\" \"Clinton\" \"went\" \"to\" \"Canada\") :TAGS #(\"NNP\" \"NNP\" \"VBD\" \"TO\" \"NNP\"))\nKGN 41 > (kbnlp::text-human-names txtobj)\n(\"Bill Clinton\")\nKGN 42 >\n\nKnowledge Graph Navigator Common Library Implementation\n223\n(loop for key being the hash-keys of\n(entity-uris:find-entities-in-text text)\nusing (hash-value value)\ndo (format t \"key: ~S value: ~S~%\" key value))\nkey: \"people\" value: ((\"Bill Clinton\" \"<http://dbpedia.org/resource/Bill_Clinton>\"))\nkey: \"countries\" value: ((\"Canada\" \"<http://dbpedia.org/resource/Canada>\"))\nNIL\nThe code using loop at the end of the last repl listing that prints keys and values of a hash table is\nfrom the Common Lisp Cookbook web site⁹³ in the section “Traversing a Hash Table.”\nDeveloping Low-Level SPARQL Utilities\nI use the standard command line curl utility program with the Common Lisp package uiop to\nmake HTML GET requests to the DBPedia public Knowledge Graph and the package drakma to\nurl-encode parts of a query. The source code is in a separate Quicklisp library located in src/sparql-\ncache/sparql.lisp. A non-caching library is also available in src/sparql/sparql.lisp.\nIn the following listing of src/sparql-cache/sparql.lisp, lines 8, 24, 39, and 55 I use some caching\ncode that we will look at later. The nested replace-all statements in lines 12-13 are a kluge to remove\nUnicode characters that occasionally caused runtime errors in the KGN application.\n1\n(in-package #:kgn)\n2\n3\n(ql:quickload \"cl-json\")\n4\n(ql:quickload \"drakma\")\n5\n6\n(defun sparql-dbpedia (query)\n7\n(let* (ret\n8\n(cr (fetch-result-dbpedia query))\n9\n(response\n10\n(or\n11\ncr\n12\n(replace-all\n13\n(replace-all\n14\n(uiop:run-program\n15\n(list\n16\n\"curl\"\n17\n(concatenate 'string\n18\n\"https://dbpedia.org/sparql?query=\"\n19\n(drakma:url-encode query :utf-8)\n20\n\"&format=json\"))\n⁹³http://cl-cookbook.sourceforge.net/hashes.html\n\nKnowledge Graph Navigator Common Library Implementation\n224\n21\n:output :string)\n22\n\"\\\\u2013\" \" \")\n23\n\"\\\\u\" \" \"))))\n24\n(save-query-result-dbpedia query response)\n25\n(ignore-errors\n26\n(with-input-from-string\n27\n(s response)\n28\n(let ((json-as-list (json:decode-json s)))\n29\n(setf\n30\nret\n31\n(mapcar #'(lambda (x)\n32\n;;(pprint x)\n33\n(mapcar #'(lambda (y)\n34\n(list (car y) (cdr (assoc :value (cdr y))))) x))\n35\n(cdr (cadddr (cadr json-as-list))))))))\n36\nret))\n37\n38\n(defun sparql-ask-dbpedia (query)\n39\n(let* ((cr (fetch-result-dbpedia query))\n40\n(response\n41\n(or\n42\ncr\n43\n(replace-all\n44\n(replace-all\n45\n(uiop:run-program\n46\n(list\n47\n\"curl\"\n48\n(concatenate 'string\n49\n\"https://dbpedia.org/sparql?query=\"\n50\n(drakma:url-encode query :utf-8)\n51\n\"&format=json\"))\n52\n:output :string)\n53\n\"\\\\u2013\" \" \")\n54\n\"\\\\u\" \" \"))))\n55\n(save-query-result-dbpedia query response)\n56\n(if\n(search \"true\" response)\n57\nt\n58\nnil)))\nThe code for replacing Unicode characters is messy but prevents problems later when we are using\nthe query results in the example application.\nThe code (json-as-list (json:decode-json s)) on line 28 converts a deeply nested JSON response\nto nested Common Lisp lists. You may want to print out the list to better understand the mapcar\n\nKnowledge Graph Navigator Common Library Implementation\n225\nexpression on lines 31-35. There is no magic to writing expressions like this, in a repl I set json-as-\nlist to the results of one query, and I spent a minute or two experimenting with the nested mapcar\nexpression to get it to work with my test case.\nThe implementation for sparql-ask-dbpedia in lines 38-58 is simpler because we don’t have to fully\nparse the returned SPARQL query results. A SPARQL ask type query returns a true/false answer\nto a query. We will use this to determine the types of entities in query text. While our NLP library\nidentifies entity types, making additional ask queries to DBPedia to verify entity types will provide\nbetter automated results.\nImplementing the Caching Layer\nWhile developing KGN and also using it as an end user, many SPARQL queries to DBPedia contain\nrepeated entity names so it makes sense to write a caching layer. We use a SQLite database “∼/.kgn_-\ncache.db” to store queries and responses.\nThe caching layer is implemented in the file src/sparql-cache/sparql.lisp and some of the relevant\ncode is listed here:\n1\n;;; SqList caching for SPARQL queries:\n2\n3\n(defvar *db-path* (pathname \"~/.kgn_cache.db\"))\n4\n5\n(defun create-dbpedia ()\n6\n(sqlite:with-open-database (d *db-path*)\n7\n(ignore-errors\n8\n(sqlite:execute-single d\n9\n\"CREATE TABLE dbpedia (query string\nPRIMARY KEY ASC, result string)\"))))\n10\n11\n(defun save-query-result-dbpedia (query result)\n12\n(sqlite:with-open-database (d *db-path*)\n13\n(ignore-errors\n14\n(sqlite:execute-to-list d\n15\n\"insert into dbpedia (query, result) values (?, ?)\"\n16\nquery result))))\n17\n(defun fetch-result-dbpedia (query)\n18\n(sqlite:with-open-database (d *db-path*)\n19\n(cadar\n20\n(sqlite:execute-to-list d\n21\n\"select * from dbpedia where query = ?\" query))))\nThis caching layer greatly speeds up my own personal use of KGN. Without caching, queries that\ncontain many entity references simply take too long to run. The UI for the KGN applications in\n\nKnowledge Graph Navigator Common Library Implementation\n226\nlater chapters have a menu option for clearing the local cache but I almost never use this option\nbecause growing a large cache that is tailored for the types of information I search for makes the\nentire system much more responsive.\nUtilities in the Main Library File kgn-common.lisp\nThe utilities in the file src/kgn-common/kgn-common.lisp can be seen in this complete code listing:\n1\n(in-package #:kgn-common)\n2\n3\n(defun pprint-results (results &key (stream t))\n4\n(pprint results stream))\n5\n6\n(defun colorize-sparql-local (s\n&key (stream nil))\n7\n\"placeholder - some applications, like the kgn-capi-ui example need to\n8\ncolorize the sparql output\"\n9\n(princ s stream))\n10\n11\n(defun check-uri (uri)\n12\n\"sloppy code fix: URIs have different forms - normalize these\"\n13\n(if (equal (type-of uri) 'cons) (setf uri (second uri)))\n14\n(entity-uris:ensure-uri-brackets uri))\n15\n16\n(defun clean-comment (comment-string)\n17\n\"When getting comment strings from DBPedia, there are parts\n18\nthat I remove for display\"\n19\n(let ((i1 (search \"(\" comment-string))\n20\n(i2 (search \")\" comment-string)))\n21\n(if (and i1 i2 (> i2 i1) (> i1 0))\n22\n(concatenate 'string (subseq comment-string 0 (- i1 1))\n23\n(subseq comment-string (+ i2 1)))\n24\n(let ((j1 (search \" / \" comment-string)))\n25\n(if j1\n26\n(let ((j2 (search \"/\" comment-string :start2 (+ j1 2))))\n27\n(if (and j1 j2 (> j2 j1) (< (+ j2 1) (length comment-string)))\n28\n(concatenate 'string (subseq comment-string 0 j1)\n29\n(subseq comment-string (+ j2 1)))\n30\ncomment-string))\n31\ncomment-string)\n32\ncomment-string))))\n33\n34\n(defun clean-results (results)\n\nKnowledge Graph Navigator Common Library Implementation\n227\n35\n\"This function is replaced when we later build GUI apps\"\n36\nresults)\n37\n38\n(defun get-name-and-description-for-uri (uri)\n39\n(let* ((sparql\n40\n(replace-all\n41\n(format nil\n42\n\"select distinct ?name ?comment { @@ ~\n43\nvalues ?nameProperty {<http://www.w3.org/2000/01/rdf-schema#label>\n44\n<http://xmlns.com/foaf/0.1/name> } . @@ ~\n45\n~A ?nameProperty ?name . @@ ~\n46\n~A <http://www.w3.org/2000/01/rdf-schema#comment>\n?comment\n.\n47\nFILTER\n(lang(?comment) = 'en') . @@ ~\n48\n} LIMIT 1\" uri uri)\n49\n\"@@\" \" \"))\n50\n(results (sparql-cache:dbpedia sparql)))\n51\n(list (second (assoc :name (car results)))\n52\n(second (assoc :comment (car results))))))\n53\n54\n;; (kgn-common::get-name-and-description-for-uri\n55\n;;\n\"<http://dbpedia.org/resource/Apple_Inc.>\")\n56\n57\n(defun ask-is-type-of (entity-uri type-value-uri) ;; both URIs expected to use surro\\\n58\nunding < > brackets for SPARQL\n59\n(let* ((sparql\n60\n(format nil\n61\n\"ASK { ~A <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> ~A }\"\n62\n(check-uri entity-uri) (check-uri type-value-uri)))\n63\n(results (sparql-cache:ask-dbpedia sparql)))\n64\n(print sparql)\n65\nresults))\n66\n67\n;; (kgn-common::ask-is-type-of\n68\n;;\n\"<http://dbpedia.org/resource/Apple_Inc.>\"\n69\n;;\n\"<http://dbpedia.org/ontology/Company>\")\n70\n71\n72\n(defun dbpedia-get-entities-by-name (name dbpedia-type schema-org-type\n73\n&key (message-stream t)\n74\n(colorize-sparql-function #'colorize-sparql-local))\n75\n;; http://www.w3.org/1999/02/22-rdf-syntax-ns#type <http://schema.org/Person>\n76\n(let* ((sparql\n77\n(format nil\n\nKnowledge Graph Navigator Common Library Implementation\n228\n78\n\"select distinct ?s ?comment {\n79\n?s ?p \\\"~A\\\"@en . @@ ~\n80\n?s <http://www.w3.org/2000/01/rdf-schema#comment>\n?comment\n. FILTER\n(lang(?com\\\n81\nment) = 'en') . @@ ~\n82\n?s <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> ~A . @@ ~\n83\n} LIMIT 15\" name dbpedia-type))\n84\n(results (sparql-cache:dbpedia (replace-all sparql \"@@\" \" \"))))\n85\n(print results)\n86\n(terpri message-stream)\n87\n(format message-stream\n88\n\"Trying to get entity by name = ~A using SPARQL with type:\"\n89\nname dbpedia-type)\n90\n(terpri message-stream)\n91\n(apply colorize-sparql-function (list sparql :stream message-stream))\n92\n(if (null results)\n93\n(let* ((sparql2\n94\n(format nil\n95\n\"select distinct ?s ?comment {\n96\n?s ?p \\\"~A\\\"@en . @@ ~\n97\n?s <http://www.w3.org/2000/01/rdf-schema#comment>\n?comment\n. FILTER\n(lang(?co\\\n98\nmment) = 'en') . @@ ~\n99\n?s <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> ~A . @@ ~\n100\n} LIMIT 15\" name schema-org-type)))\n101\n(format\n102\nt\n103\n\"No results for ~A for last SPARQL query using type ~A so trying type ~A\"\\\n104\nname dbpedia-type schema-org-type)\n105\n(terpri message-stream)\n106\n(setf results (sparql-cache:dbpedia (replace-all sparql2 \"@@\" \" \")))\n107\n(if (null results)\n108\n(format\n109\nt\n110\n\"No results for ~A for last SPARQL query using type ~A\"\n111\nname schema-org-type)\n112\n(let* ((filtered (remove-if\n113\n#'(lambda (x)\n114\n(or\n115\n(search \",\" (cadar x))\n116\n(and\n117\n(not (equal (first x) :comment))\n118\n(not (search \"/resource/\" (cadar x))))))\n119\nresults))\n120\n(uris (remove-duplicates\n\nKnowledge Graph Navigator Common Library Implementation\n229\n121\n(map\n122\n'list\n123\n#'(lambda (x)\n124\n(list (concatenate 'string \"<\" (cadar x) \">\") (cadad\\\n125\nr x)))\n126\nfiltered) :test #'equal)))\n127\n(format t \"~%~%********** dbpedia-get-entities-by-name: uris:~%\")\n128\n(pprint uris) (terpri)\n129\nuris))))\n130\nresults))\n131\n132\n;; (kgn-common:dbpedia-get-entities-by-name\n133\n;;\n\"Bill Gates\" \"<http://dbpedia.org/ontology/Person>\"\n134\n;;\n\"<http://schema.org/Person>\" :message-stream nil)\n135\n;; in above, pass t for message-stream to see generated SPARQL queries\n136\n137\n(defun dbpedia-get-person-detail (person-uri-raw\n138\n&key (message-stream t)\n139\n(colorize-sparql-function #'colorize-sparql-local))\n140\n;; http://dbpedia.org/ontology/birthPlace\n141\n(let* ((person-uri (check-uri person-uri-raw))\n142\n(query\n143\n(format nil\n144\n\"SELECT DISTINCT ?label ?comment@@ ~\n145\n(GROUP_CONCAT (DISTINCT ?birthplace; SEPARATOR=' | ') AS ?birthplace) @@ ~\n146\n(GROUP_CONCAT (DISTINCT ?almamater; SEPARATOR=' | ') AS ?almamater) @@ ~\n147\n(GROUP_CONCAT (DISTINCT ?spouse; SEPARATOR=' | ') AS ?spouse) { @@ ~\n148\n~A <http://www.w3.org/2000/01/rdf-schema#comment>\n?comment .@@\n149\nFILTER\n(lang(?comment) = 'en') . @@ ~\n150\nOPTIONAL { ~A <http://dbpedia.org/ontology/birthPlace> ?birthplace } . @@ ~\n151\nOPTIONAL { ~A <http://dbpedia.org/ontology/almaMater> ?almamater } . @@ ~\n152\nOPTIONAL { ~A <http://dbpedia.org/ontology/spouse> ?spouse } . @@ ~\n153\nOPTIONAL { ~A\n<http://www.w3.org/2000/01/rdf-schema#label> ?label .@@ ~\n154\nFILTER\n(lang(?label) = 'en') } @@ ~\n155\n} LIMIT 10@@\" person-uri person-uri person-uri person-uri person-uri))\n156\n(results (sparql-cache:dbpedia\n(replace-all query \"@@\" \" \"))))\n157\n(format message-stream \"~%SPARQL to get PERSON data for ~A:~%~%\" person-uri)\n158\n(apply colorize-sparql-function (list query :stream message-stream))\n159\n(format message-stream \"~%\")\n160\n;;results))\n161\n(clean-results results)))\n162\n163\n;; (kgn-common:dbpedia-get-person-detail \"<http://dbpedia.org/resource/Bill_Gates>\")\n\nKnowledge Graph Navigator Common Library Implementation\n230\n164\n165\n(defun dbpedia-get-company-detail (company-uri-raw\n166\n&key (message-stream t)\n167\n(colorize-sparql-function #'colorize-sparql-local))\n168\n(let* ((company-uri (check-uri company-uri-raw))\n169\n(query\n170\n(format nil\n171\n\"SELECT DISTINCT ?label ?comment (GROUP_CONCAT (DISTINCT ?industry; SEPARATOR=' | ')\\\n172\nAS ?industry)@@ ~\n173\n(GROUP_CONCAT (DISTINCT ?netIncome; SEPARATOR=' | ') AS ?netIncome)@@ ~\n174\n(GROUP_CONCAT (DISTINCT ?numberOfEmployees; SEPARATOR=' | ') AS ?numberOfEmployees\\\n175\n) {@@ ~\n176\n~A <http://www.w3.org/2000/01/rdf-schema#comment>\n?comment .@@\n177\nFILTER\n(lang(?comment) = 'en') .@@ ~\n178\nOPTIONAL { ~A <http://dbpedia.org/ontology/industry> ?industry } .@@\n~\n179\nOPTIONAL { ~A <http://dbpedia.org/ontology/netIncome> ?netIncome } .@@\n~\n180\nOPTIONAL { ~A <http://dbpedia.org/ontology/numberOfEmployees> ?numberOfEmployees }\\\n181\n.@@\n~\n182\nOPTIONAL { ~A <http://www.w3.org/2000/01/rdf-schema#label> ?label . FILTER (lang(?\\\n183\nlabel) = 'en') } @@ ~\n184\n} LIMIT 30@@\"\n185\ncompany-uri company-uri company-uri company-uri company-uri))\n186\n(results (sparql-cache:dbpedia\n(replace-all query \"@@\" \" \"))))\n187\n(format message-stream \"~%SPARQL to get COMPANY data for ~A:~%~%\" company-uri)\n188\n(apply colorize-sparql-function (list query :stream message-stream))\n189\n(format message-stream \"~%\")\n190\n(clean-results results)))\n191\n192\n;; (kgn-common:dbpedia-get-company-detail \"<http://dbpedia.org/resource/Microsoft>\")\n193\n194\n(defun dbpedia-get-country-detail (country-uri-raw\n195\n&key (message-stream t)\n196\n(colorize-sparql-function #'colorize-sparql-local))\n197\n(let* ((country-uri (check-uri country-uri-raw))\n198\n(query\n199\n(format nil\n200\n\"SELECT DISTINCT ?label ?comment (GROUP_CONCAT (DISTINCT ?areaTotal; SEPARATOR=' | '\\\n201\n) AS ?areaTotal)@@ ~\n202\n(GROUP_CONCAT (DISTINCT ?populationDensity; SEPARATOR=' | ') AS ?populationDensity\\\n203\n) {@@ ~\n204\n~A <http://www.w3.org/2000/01/rdf-schema#comment>\n?comment .@@\n205\nFILTER\n(lang(?comment) = 'en') .@@ ~\n206\nOPTIONAL { ~A <http://dbpedia.org/ontology/areaTotal> ?areaTotal } .@@\n~\n\nKnowledge Graph Navigator Common Library Implementation\n231\n207\nOPTIONAL { ~A <http://dbpedia.org/ontology/populationDensity> ?populationDensity }\\\n208\n.@@\n~\n209\nOPTIONAL { ~A <http://www.w3.org/2000/01/rdf-schema#label> ?label . }@@ ~\n210\n} LIMIT 30@@\"\n211\ncountry-uri country-uri country-uri country-uri country-uri))\n212\n(results (sparql-cache:dbpedia\n(replace-all query \"@@\" \" \"))))\n213\n(format message-stream \"~%SPARQL to get COUNTRY data for ~A:~%~%\" country-uri)\n214\n(apply colorize-sparql-function (list query :stream message-stream))\n215\n(format message-stream \"~%\")\n216\n(clean-results results)))\n217\n218\n;; (kgn-common:dbpedia-get-country-detail \"<http://dbpedia.org/resource/Canada>\")\n219\n220\n(defun dbpedia-get-city-detail (city-uri-raw\n221\n&key (message-stream t)\n222\n(colorize-sparql-function #'colorize-sparql-local))\n223\n(let* ((city-uri (check-uri city-uri-raw))\n224\n(query\n225\n(format\n226\nnil\n227\n\"SELECT DISTINCT ?label ?comment @@ ~\n228\n(GROUP_CONCAT (DISTINCT ?latitude_longitude; SEPARATOR=' | ')\nAS ?latitude_longit\\\n229\nude) @@ ~\n230\n(GROUP_CONCAT (DISTINCT ?populationDensity; SEPARATOR=' | ') AS ?populationDensity\\\n231\n) @@ ~\n232\n(GROUP_CONCAT (DISTINCT ?country; SEPARATOR=' | ') AS ?country) { @@ ~\n233\n~A <http://www.w3.org/2000/01/rdf-schema#comment>\n?comment . FILTER\n(lang(?comme\\\n234\nnt) = 'en') . @@ ~\n235\nOPTIONAL { ~A <http://www.w3.org/2003/01/geo/wgs84_pos#geometry> ?latitude_longitu\\\n236\nde } . @@ ~\n237\nOPTIONAL { ~A <http://dbpedia.org/ontology/PopulatedPlace/populationDensity> ?popu\\\n238\nlationDensity } . @@ ~\n239\nOPTIONAL { ~A <http://dbpedia.org/ontology/country> ?country } .@@ ~\n240\nOPTIONAL { ~A <http://www.w3.org/2000/01/rdf-schema#label> ?label . } @@ ~\n241\n} LIMIT 30@@\"\n242\ncity-uri city-uri city-uri city-uri city-uri))\n243\n(results (sparql-cache:dbpedia (replace-all query \"@@\" \" \"))))\n244\n(format message-stream \"~%SPARQL to get CITY data for ~A:~%~%\" city-uri)\n245\n(apply colorize-sparql-function (list query :stream message-stream))\n246\n(format message-stream \"~%\")\n247\n(clean-results results)))\n248\n249\n;; (kgn-common:dbpedia-get-city-detail \"<http://dbpedia.org/resource/London>\")\n\nKnowledge Graph Navigator Common Library Implementation\n232\n250\n251\n(defun dbpedia-get-product-detail (product-uri-raw\n252\n&key (message-stream t)\n253\n(colorize-sparql-function #'colorize-sparql-local))\n254\n(let* ((product-uri (check-uri product-uri-raw))\n255\n(query\n256\n(format\n257\nnil\n258\n\"SELECT DISTINCT ?label ?comment {\n@@ ~\n259\n~A <http://www.w3.org/2000/01/rdf-schema#comment>\n?comment . FILTER\n(lang(?comme\\\n260\nnt) = 'en') . @@ ~\n261\nOPTIONAL { ~A <http://www.w3.org/2000/01/rdf-schema#label> ?label . } ~\n262\n} LIMIT 30@@\"\n263\nproduct-uri product-uri))\n264\n(results (sparql-cache:dbpedia (replace-all query \"@@\" \" \"))))\n265\n(format message-stream \"~%SPARQL to get PRODUCT data for ~A:~%~%\" product-uri)\n266\n(apply colorize-sparql-function (list query :stream message-stream))\n267\n(format message-stream \"~%\")\n268\n(clean-results results)))\n269\n270\n;; (kgn-common:dbpedia-get-product-detail \"<http://dbpedia.org/resource/Pepsi>\")\n271\n272\n273\n(defun dbpedia-get-relationships (s-uri o-uri) ;;\n&key (message-stream t))\n274\n(let* ((query\n275\n(format\n276\nnil\n277\n\"SELECT DISTINCT ?p { ~A ?p ~A . FILTER (!regex(str(?p), 'wikiPage', 'i'))} LIMIT 5\"\n278\n(check-uri s-uri) (check-uri\no-uri)))\n279\n(results (sparql-cache:dbpedia query)))\n280\n(alexandria:flatten (map 'list\n281\n#'(lambda (x)\n282\n(format nil \"~{<~A>~}\" (cdar x)))\n283\nresults))))\n284\n285\n;; (kgn-common::dbpedia-get-relationships\n286\n;;\n\"<http://dbpedia.org/resource/Bill_Gates>\"\n287\n;;\n\"<http://dbpedia.org/resource/Microsoft>\")\n288\n289\n(defun entities (text)\n290\n(let ((txt-obj (kbnlp:make-text-object text)))\n291\n(list (kbnlp::text-human-names txt-obj) (kbnlp::text-place-names txt-obj) (kbnlp\\\n292\n::text-company-names txt-obj))))\n\nKnowledge Graph Navigator Common Library Implementation\n233\n293\n294\n;; (kgn-common::entities \"Bill Clinton went to Canada\")\n295\n296\n(defun entities-dbpedia (text)\n297\n(let ((e-hash (entity-uris:find-entities-in-text text)))\n298\n(list\n299\n(gethash \"people\" e-hash)\n300\n(gethash \"companies\" e-hash)\n301\n(gethash \"countries\" e-hash)\n302\n(gethash \"cities\" e-hash))))\n303\n304\n;; (kgn-common::entities-dbpedia \"Bill Clinton went to Canada\")\nWrap-up\nThis is a long example application for a book, split between this chapter and the next two chapters\noffering different user interface implementations.\nI got the idea for the KGN application because I was spending quite a bit of time manually setting up\nSPARQL queries for DBPedia (and other public sources like WikiData) and I wanted to experiment\nwith partially automating this process. Now in the next two chapters we will write user interfaces\nfor this KGN common library.\n\nKnowledge Graph Navigator\nText-Based User Interface\nWe developed the The Knowledge Graph Navigator (which I will often refer to as KGN) common\nlibrary in the last chapter. Here we write a simple console or text-based user interface for the library.\nIn later chapters we implement UIs using LispWorks CAPI, McCLIM, and Franz Common Graphics.\nThis Quicklisp library can be found in a separate GitHub repository https://github.com/mark-\nwatson/kgn-text-ui⁹⁴ and contains the files:\n• kgn-text-ui.asd - specifies dependencies, including the KGN common library\n• kgn-text-ui.lisp - Contains the complete user interface\n• package.lisp - specifies dependencies, including the KGN common library\nWe start by looking at sample output using the text user interface and then look at the implementa-\ntion.\nExample Output\nWe will look at a very simple example query Bill Gates worked at Microsoft and his competitor\nwas IBM that only contains a few entities. In practice, I usually use queries with five to ten entities to\nget more discovered relationships. I remove a lot of the generated output in the following listing for\nbrevity, especially the many generated SPARQL queries that the code generates and uses (comments\non the output appear after this listing):\n1\n$ sbcl\n2\n*(ql:quickload :kgn-text-ui)\n3\n; Loading \"kgn-common\"\n4\n; Loading \"sqlite\"\n5\n; Loading \"cl-json\"\n6\n; Loading \"drakma\"\n7\n8\n* (kgn-text-ui:kgn-text-ui)\n9\n10\n\"Enter entity names (people, places, companies, etc.\":\n11\nBill Gates worked at Microsoft and his competitor was IBM\n⁹⁴https://github.com/mark-watson/kgn-text-ui\n\nKnowledge Graph Navigator Text-Based User Interface\n235\n12\n13\n- - - - Enter zero or more indices for your desired selections:\n14\n15\n0\n-\n\"William Henry Gates III (born October 28, 1955) is an American business magn\\\n16\nate, software developer, investor, author, and philanthropist. He is a co-founder of\\\n17\nMicrosoft, along with his late childhood friend Paul Allen. During his career at Mi\\\n18\ncrosoft, Gates held the positions of chairman, chief executive officer (CEO), presid\\\n19\nent and chief software architect, while also being the largest individual shareholde\\\n20\nr until May 2014. He is considered one of the best known entrepreneurs of the microc\\\n21\nomputer revolution of the 1970s and 1980s.\"\n22\n23\n1\n-\n\"Harry Roy Lewis (born 1947) is an American computer scientist, mathe 00ADma \\\n24\n00ADti 00ADcian, and uni 00ADver 00ADsity admin 00ADi 00ADstra 00ADtor known for his\\\n25\nresearch in com 00ADpu 00ADta 00ADtional logic, textbooks in theoretical computer s\\\n26\ncience, and writings on computing, higher education, and technology. He is Gordon Mc\\\n27\nKay Professor of Computer Science at Harvard University, and was Dean of Harvard Col\\\n28\nlege from 1995 to 2003. A new professorship in Engineering and Applied Sciences, end\\\n29\nowed by a former student, will be named for Lewis and his wife upon their retirement\\\n30\ns.\"\n31\n32\n2\n-\n\"Cascade Investment, L.L.C. is an American holding company and private invest\\\n33\nment firm headquartered in Kirkland, Washington, United States. It is controlled by \\\n34\nBill Gates, and managed by Michael Larson. More than half of Gates' fortune is held \\\n35\nin assets outside his holding of Microsoft shares. Cascade is the successor company \\\n36\nto Dominion Income Management, the former investment vehicle for Gates' holdings, wh\\\n37\nich was managed by convicted felon Andrew Evans.\"\n38\n39\n3\n-\n\"Jerry P. Dyer (born May 3, 1959) is an American politician and former law en\\\n40\nforcement officer. He is the 26th and current mayor of Fresno, California. Previousl\\\n41\ny, he served as the chief of the Fresno Police Department.\"\n42\n43\n0\n44\n45\n- - - - Enter zero or more indices for your desired selections:\n46\n47\n0\n-\n\"Kenexa, an IBM Company, provides employment and retention services. This inc\\\n48\nludes recruitment process outsourcing onboarding tools, employee assessment, abiliti\\\n49\nes assessment for employment candidates (Kenexa Prove It); and Kenexa Interview Buil\\\n50\nder, a structured interview archive with example questions.\"\n51\n52\n1\n-\n\"Sequent Computer Systems was a computer company that designed and manufactur\\\n53\ned multiprocessing computer systems. They were among the pioneers in high-performanc\\\n54\ne symmetric multiprocessing (SMP) open systems, innovating in both hardware (e.g., c\\\n\nKnowledge Graph Navigator Text-Based User Interface\n236\n55\nache management and interrupt handling) and software (e.g., read-copy-update). Vesti\\\n56\nges of Sequent's innovations live on in the form of data clustering software from Po\\\n57\nlyServe (subsequently acquired by HP), various projects within OSDL, IBM contributio\\\n58\nns to the Linux kernel, and claims in the SCO v. IBM lawsuit.\"\n59\n60\n2\n-\n\"i2 Limited was the UK-based arm of software company i2 Group which produced \\\n61\nvisual intelligence and investigative analysis software for military intelligence, l\\\n62\naw enforcement and commercial agencies. After a number of acquisitions, in 2011 it b\\\n63\necame part of IBM.\"\n64\n65\n3\n-\n\"The International Technology Alliance in Distributed Analytics and Informati\\\n66\non Sciences (DAIS-ITA) is a research program initiated by the UK Ministry of Defence\\\n67\n(United Kingdom) (MOD) and the US Army Research Laboratory (ARL), in September 2016\\\n68\n. It is led by IBM Research in the U.S. and IBM Hursley in the UK. DAIS ITA is the s\\\n69\necond International Technology Alliance started by the two countries, succeeding the\\\n70\nprevious ten year alliance NIS-ITA, which was of similar nature.\"\n71\n72\n4\n-\n\"The International Technology Alliance in Network and Information Sciences (N\\\n73\nIS-ITA) was a research program initiated by the UK Ministry of Defence (United Kingd\\\n74\nom) (MoD) and the US Army Research Laboratory (ARL), which was active for 10 years f\\\n75\nrom May 2006 to May 2016. It was led by IBM Research in the U.S. and IBM Hursley in \\\n76\nthe UK. NIS ITA was the first International Technology Alliance started by the two c\\\n77\nountries.\"\n78\n79\n5\n-\n\"Applix Inc. was a computer software company founded in 1983 based in Westbor\\\n80\nough, Massachusetts that published Applix TM1, a multi-dimensional online analytical\\\n81\nprocessing (MOLAP) database server, and related presentation tools, including Appli\\\n82\nx Web and Applix Executive Viewer. Together, Applix TM1, Applix Web and Applix Execu\\\n83\ntive Viewer were the three core components of the Applix Business Analytics Platform\\\n84\n. (Executive Viewer was subsequently discontinued by IBM.)\"\n85\n86\n6\n-\n\"Ounce Labs (an IBM company) is a Waltham, Massachusetts-based security softw\\\n87\nare vendor. The company was founded in 2002 and created a software analysis product \\\n88\nthat analyzes source code to identify and remove security vulnerabilities. The secur\\\n89\nity software looks for a range of vulnerabilities that leave an application open to \\\n90\nattack. Customers have included GMAC, Lockheed Martin, and the U.S. Navy. On July 28\\\n91\n, 2009, Ounce was acquired by IBM, for an undisclosed sum, with the intention of int\\\n92\negrating it into IBM's Rational Software business.\"\n93\n94\n7\n-\n\"IBM Watson Health is a digital tool that helps clients facilitate medical re\\\n95\nsearch, clinical research, and healthcare solutions, through the use of artificial i\\\n96\nntelligence, data, analytics, cloud computing, and other advanced information techno\\\n97\nlogy. It is a division of the International Business Machines Corporation, (IBM), an\\\n\nKnowledge Graph Navigator Text-Based User Interface\n237\n98\nAmerican multinational information technology company headquartered in Armonk, New \\\n99\nYork.\"\n100\n101\n8\n-\n\"International Business Machines Corporation (IBM) is an American multination\\\n102\nal technology corporation headquartered in Armonk, New York, with operations in over\\\n103\n171 countries. The company began in 1911, founded in Endicott, New York by trust bu\\\n104\nsinessman Charles Ranlett Flint, as the Computing-Tabulating-Recording Company (CTR)\\\n105\nand was renamed \\\"International Business Machines\\\" in 1924. IBM is incorporated in\\\n106\nNew York.\"\n107\n108\n9\n-\n\"Microsoft Corporation is an American multinational technology corporation wh\\\n109\nich produces computer software, consumer electronics, personal computers, and relate\\\n110\nd services. Its best known software products are the Microsoft Windows line of opera\\\n111\nting systems, the Microsoft Office suite, and the Internet Explorer and Edge web bro\\\n112\nwsers. Its flagship hardware products are the Xbox video game consoles and the Micro\\\n113\nsoft Surface lineup of touchscreen personal computers. Microsoft ranked No. 21 in th\\\n114\ne 2020 Fortune 500 rankings of the largest United States corporations by total reven\\\n115\nue; it was the world's largest software maker by revenue as of 2016. It is considere\\\n116\nd one of the Big Five companies in the U.S. information technology industry, along w\\\n117\nith Amazon, Google (Alphabet), Apple, and Facebook (\"\n118\n119\n10\n-\n\"The CSS Working Group (Cascading Style Sheets Working Group) is a working g\\\n120\nroup created by the World Wide Web Consortium (W3C) in 1997, to tackle issues that h\\\n121\nad not been addressed with CSS level 1. As of December 2019, the CSSWG had 142 membe\\\n122\nrs. The working group is co-chaired by and .\"\n123\n124\n11\n-\n\"The AMD Professional Gamers League (PGL), founded around 1997, was one of t\\\n125\nhe first professional computer gaming eSports leagues. The PGL was run by Total Ente\\\n126\nrtainment Network and was sponsored by AMD. The first professional tournament they h\\\n127\neld was for StarCraft in September 1997. The league was official unveiled at a press\\\n128\nconference at Candlestick Park in San Francisco on November 3, 1997. It was sponsor\\\n129\ned by Microsoft, Nvidia, and Levi Strauss & Co. The organization raised over $1.2mil\\\n130\nUSD in sponsorship money.\"\n131\n132\n12\n-\n\"Secure Islands Technologies Ltd. was an Israeli privately held technology c\\\n133\nompany headquartered in Beit Dagan which was subsequently acquired by Microsoft. The\\\n134\ncompany develops and markets Information Protection and Control (IPC) solutions.\"\n135\n136\n13\n-\n\"Microsoft Innovation Centers (MICs) are local government organizations, uni\\\n137\nversities, industry organizations, or software or hardware vendors who partner with \\\n138\nMicrosoft with a common goal to foster the growth of local software economies. These\\\n139\nare state of the art technology facilities which are open to students, developers, \\\n140\nIT professionals, entrepreneurs, startups and academic researchers. While each Cente\\\n\nKnowledge Graph Navigator Text-Based User Interface\n238\n141\nr tunes its programs to local needs, they all provide similar content and services d\\\n142\nesigned to accelerate technology advances and stimulate local software economies thr\\\n143\nough skills and professional training, industry partnerships and innovation. As of 1\\\n144\n0 September 2010, there are 115 Microsoft Innovation Centers worldwide, most of whic\\\n145\nh are open to the public. Recently it was reported th\"\n146\n147\n14\n-\n\"Press Play ApS was a Danish video game development studio based in central \\\n148\nCopenhagen in Denmark. Since 2006, Press Play have released five titles, including t\\\n149\nhe Max & the Magic Marker, Max: The Curse of Brotherhood and Kalimba. On November 10\\\n150\n, 2016, Flashbulb acquired Press Play and its library of games to republish under th\\\n151\ne Flashbulb name including Kalimba, Tentacles: Enter the Mind, and Max: The Curse of\\\n152\nBrotherhood.\"\n153\n154\n8 9\n155\n156\n- - - ENTITY TYPE: people - - -\n157\n158\nSPARQL to get PERSON data for <http://dbpedia.org/resource/Bill_Gates>:\n159\n160\n\"SELECT DISTINCT ?label ?comment@@ (GROUP_CONCAT (DISTINCT ?birthplace; SEPARATOR=' \\\n161\n| ') AS ?birthplace) @@ (GROUP_CONCAT (DISTINCT ?almamater; SEPARATOR=' | ') AS ?alm\\\n162\namater) @@ (GROUP_CONCAT (DISTINCT ?spouse; SEPARATOR=' | ') AS ?spouse) { @@ <http:\\\n163\n//dbpedia.org/resource/Bill_Gates> <http://www.w3.org/2000/01/rdf-schema#comment>\n?\\\n164\ncomment .@@\n165\nFILTER\n(lang(?comment) = 'en') . @@ OPTIONAL { <http://d\\\n166\nbpedia.org/resource/Bill_Gates> <http://dbpedia.org/ontology/birthPlace> ?birthplace\\\n167\n} . @@ OPTIONAL { <http://dbpedia.org/resource/Bill_Gates> <http://dbpedia.org/onto\\\n168\nlogy/almaMater> ?almamater } . @@ OPTIONAL { <http://dbpedia.org/resource/Bill_Gates\\\n169\n> <http://dbpedia.org/ontology/spouse> ?spouse } . @@ OPTIONAL { <http://dbpedia.org\\\n170\n/resource/Bill_Gates>\n<http://www.w3.org/2000/01/rdf-schema#label> ?label .@@ FILTE\\\n171\nR\n(lang(?label) = 'en') } @@ } LIMIT 10@@\"\n172\n173\n174\nlabel: Bill Gates\n175\n176\ncomment: William Henry Gates III (born October 28, 1955) is an American business mag\\\n177\nnate, software developer, investor, author, and philanthropist. He is a co-founder o\\\n178\nf Microsoft, along with his late childhood friend Paul Allen. During his career at M\\\n179\nicrosoft, Gates held the positions of chairman, chief executive officer (CEO), presi\\\n180\ndent and chief software architect, while also being the largest individual sharehold\\\n181\ner until May 2014. He is considered one of the best known entrepreneurs of the micro\\\n182\ncomputer revolution of the 1970s and 1980s.\n183\n\nKnowledge Graph Navigator Text-Based User Interface\n239\n184\nbirthplace: http://dbpedia.org/resource/Seattle | http://dbpedia.org/resource/Washin\\\n185\ngton_(state)\n186\n187\nalmamater:\n188\n189\nspouse: http://dbpedia.org/resource/Melinda_French_Gates\n190\n191\n- - - ENTITY TYPE: companies - - -\n192\n193\nSPARQL to get COMPANY data for <http://dbpedia.org/resource/IBM>:\n194\n195\n196\n\"SELECT DISTINCT ?label ?comment (GROUP_CONCAT (DISTINCT ?industry; SEPARATOR=' | ')\\\n197\nAS ?industry)@@ (GROUP_CONCAT (DISTINCT ?netIncome; SEPARATOR=' | ') AS ?netIncome)\\\n198\n@@ (GROUP_CONCAT (DISTINCT ?numberOfEmployees; SEPARATOR=' | ') AS ?numberOfEmployee\\\n199\ns) {@@ <http://dbpedia.org/resource/IBM> <http://www.w3.org/2000/01/rdf-schema#comme\\\n200\nnt>\n?comment .@@\n201\nFILTER\n(lang(?comment) = 'en') .@@ OPTIONAL { <http://db\\\n202\npedia.org/resource/IBM> <http://dbpedia.org/ontology/industry> ?industry } .@@\nOPTI\\\n203\nONAL { <http://dbpedia.org/resource/IBM> <http://dbpedia.org/ontology/netIncome> ?ne\\\n204\ntIncome } .@@\nOPTIONAL { <http://dbpedia.org/resource/IBM> <http://dbpedia.org/onto\\\n205\nlogy/numberOfEmployees> ?numberOfEmployees } .@@\nOPTIONAL { <http://dbpedia.org/res\\\n206\nource/IBM> <http://www.w3.org/2000/01/rdf-schema#label> ?label . FILTER (lang(?label\\\n207\n) = 'en') } @@ } LIMIT 30@@\"\n208\n209\n210\nlabel: IBM\n211\n212\ncomment: International Business Machines Corporation (IBM) is an American multinatio\\\n213\nnal technology corporation headquartered in Armonk, New York, with operations in ove\\\n214\nr 171 countries. The company began in 1911, founded in Endicott, New York by trust b\\\n215\nusinessman Charles Ranlett Flint, as the Computing-Tabulating-Recording Company (CTR\\\n216\n) and was renamed \"International Business Machines\" in 1924. IBM is incorporated in \\\n217\nNew York.\n218\n219\nindustry: http://dbpedia.org/resource/Artificial_intelligence | http://dbpedia.org/r\\\n220\nesource/Automation | http://dbpedia.org/resource/Blockchain | http://dbpedia.org/res\\\n221\nource/Cloud_computing | http://dbpedia.org/resource/Computer_hardware | http://dbped\\\n222\nia.org/resource/Quantum_computing | http://dbpedia.org/resource/Robotics | http://db\\\n223\npedia.org/resource/Software\n224\n225\nnet-income: 5.59E9\n226\n\nKnowledge Graph Navigator Text-Based User Interface\n240\n227\nnumber-of-employees: 345900\n228\n229\nSPARQL to get COMPANY data for <http://dbpedia.org/resource/Microsoft>:\n230\n231\n232\n\"SELECT DISTINCT ?label ?comment (GROUP_CONCAT (DISTINCT ?industry; SEPARATOR=' | ')\\\n233\nAS ?industry)@@ (GROUP_CONCAT (DISTINCT ?netIncome; SEPARATOR=' | ') AS ?netIncome)\\\n234\n@@ (GROUP_CONCAT (DISTINCT ?numberOfEmployees; SEPARATOR=' | ') AS ?numberOfEmployee\\\n235\ns) {@@ <http://dbpedia.org/resource/Microsoft> <http://www.w3.org/2000/01/rdf-schema\\\n236\n#comment>\n?comment .@@\n237\nFILTER\n(lang(?comment) = 'en') .@@ OPTIONAL { <http://db\\\n238\npedia.org/resource/Microsoft> <http://dbpedia.org/ontology/industry> ?industry } .@@\\\n239\nOPTIONAL { <http://dbpedia.org/resource/Microsoft> <http://dbpedia.org/ontology/ne\\\n240\ntIncome> ?netIncome } .@@\nOPTIONAL { <http://dbpedia.org/resource/Microsoft> <http:\\\n241\n//dbpedia.org/ontology/numberOfEmployees> ?numberOfEmployees } .@@\nOPTIONAL { <http\\\n242\n://dbpedia.org/resource/Microsoft> <http://www.w3.org/2000/01/rdf-schema#label> ?lab\\\n243\nel . FILTER (lang(?label) = 'en') } @@ } LIMIT 30@@\"\n244\n245\n246\nlabel: Microsoft\n247\n248\ncomment: Microsoft Corporation is an American multinational technology corporation w\\\n249\nhich produces computer software, consumer electronics, personal computers, and relat\\\n250\ned services. Its best known software products are the Microsoft Windows line of oper\\\n251\nating systems, the Microsoft Office suite, and the Internet Explorer and Edge web br\\\n252\nowsers. Its flagship hardware products are the Xbox video game consoles and the Micr\\\n253\nosoft Surface lineup of touchscreen personal computers. Microsoft ranked No. 21 in t\\\n254\nhe 2020 Fortune 500 rankings of the largest United States corporations by total reve\\\n255\nnue; it was the world's largest software maker by revenue as of 2016. It is consider\\\n256\ned one of the Big Five companies in the U.S. information technology industry, along \\\n257\nwith Amazon, Google (Alphabet), Apple, and Facebook (\n258\n259\nindustry: http://dbpedia.org/resource/Cloud_computing | http://dbpedia.org/resource/\\\n260\nComputer_hardware | http://dbpedia.org/resource/Consumer_electronics | http://dbpedi\\\n261\na.org/resource/Corporate_venture_capital | http://dbpedia.org/resource/Internet | ht\\\n262\ntp://dbpedia.org/resource/Social_networking_service | http://dbpedia.org/resource/So\\\n263\nftware_development | http://dbpedia.org/resource/Video_game_industry\n264\n265\nnet-income: 6.06E10\n266\n267\nnumber-of-employees: 182268\n268\n269\nDISCOVERED RELATIONSHIP LINKS:\n\nKnowledge Graph Navigator Text-Based User Interface\n241\n270\n271\n<http://dbpedia.org/resource/Bill_Gates>\n272\n<http://dbpedia.org/ontology/knownFor>\n273\n<http://dbpedia.org/resource/Microsoft> .\n274\n275\n276\n<http://dbpedia.org/resource/Microsoft>\n277\n<http://dbpedia.org/ontology/foundedBy>\n278\n<http://dbpedia.org/resource/Bill_Gates> .\n279\n280\n281\n<http://dbpedia.org/resource/Microsoft>\n282\n<http://dbpedia.org/property/founders>\n283\n<http://dbpedia.org/resource/Bill_Gates> .\n284\n285\n\"Enter entity names (people, places, companies, etc.\":\nOn line 10 I input a test phrase “Bill Gates worked at Microsoft and his competitor was IBM.” In lines\n13-41 the test program prints out matching human entities from DBPedia that are indexed starting\nat 0. On line 43 I entered 0 to choose just the first entity “William Henry Gates III”.\nThe prompt on line 45 asks the user to enter the indices for the company DBPedia entities they want\nto use. These companies are listed in lines 47-152. On line 154 I entered “8 9” to select two entities\nto use.\nLines 156-171 show the automatically generated SPARQL query to get information about Bill Gates.\nThis information is printed on lines 174-189. I list more generated SPARQL queries and results (which\nwe will not discuss further).\nLines 269-283 show discovered links found between the entities in the input text.\nIn the LispWorks CAPI user interface developed in the next chapter I use two text output stream\nwindow panes, one for the generated SPARQL and one for the results.\nText User Interface Implementation\nWe will skip looking at the kgn-text-ui.asd and package.lisp files for this library but look at src/kgn-\ntext-ui/kgn-text-ui.lisp in its entirety. When entities are identified in input text we find candidate\nDBPedia entity URIs that we present to the user. We precede each entire DBPedia description with\nan index starting at 0. The user enters the indices for entities to further process. For example, in the\nexample listing in the previous section I entered “8 9” to indicate two company URIs.\n\nKnowledge Graph Navigator Text-Based User Interface\n242\n1\n(in-package #:kgn-text-ui)\n2\n3\n(defun pprint-results (results)\n4\n(dolist (result (car results))\n5\n(terpri)\n6\n(format t\n\"~A:\" (first result))\n7\n(format t \" ~A~%\" (second result))))\n8\n9\n10\n(defun multiple-selections (sel-list)\n11\n(if (not (null sel-list))\n12\n(let ()\n13\n(pprint sel-list)\n14\n(format t\n15\n\"~%- - - - Enter zero or more indices for your desired selections:~%~%\")\n16\n(let ((count 0))\n17\n(dolist (sel sel-list)\n18\n(format t \"~A\n-\n~S ~%~%\" count (cadr (assoc :comment (car sel))))\n19\n(setf count (1+ count))))\n20\n(let* ((line (read-line))\n21\n(indices\n22\n(if (> (length line) 0)\n23\n(mapcar\n24\n#'parse-integer\n25\n(myutils:tokenize-string line)))))\n26\n(print indices)\n27\n;(dolist (index indices)\n28\n;\n(setf ret (cons (nth index str-list)\n29\nindices))))\n30\n31\n;; (kgn-text-ui::multiple-selections\n32\n;;\n'(\"Option 1\" \"Option 2\" \"And yet another option 3\"))\n33\n34\n35\n(defun prompt-selection-list (a-list-of-choices)\n36\n;; e.g., '((:people ((\"11\" \"data1\")\n(\"22\" \"data2\"))) (:places ((\"p1\" \"data3\"))))\n37\n(let (ret)\n38\n(dolist (choice a-list-of-choices)\n39\n(setf choice (remove-if #'null choice))\n40\n(let* ((topic-type (car choice))\n41\n(choice-list-full (rest choice))\n42\n(choice-list (remove-duplicates\n43\n(map 'list #'(lambda (z)\n\nKnowledge Graph Navigator Text-Based User Interface\n243\n44\n(list\n45\nz\n46\n(string-shorten\n47\n(kgn-common:clean-comment\n48\n(kgn-common:clean-comment (cadr z)))\n49\n140 :first-remove-stop-words t)))\n50\n;; top level list flatten:\n51\n(apply #'append choice-list-full))\n52\n:test #'equal)))\n53\n(let (ret2\n54\n(dialog-results (multiple-selections choice-list)))\n55\n(dolist (index dialog-results)\n56\n(setf ret2 (cons (nth index choice-list) ret2)))\n57\n(if (> (length ret2) 0)\n58\n(setf ret (cons (list topic-type (reverse ret2)) ret))))))\n59\n(reverse ret)))\n60\n61\n;; (kgn-text-ui::prompt-selection-list\n62\n;;\n'((:people ((\"11\" \"data1\")\n(\"22\" \"data2\")))\n63\n;;\n(:places ((\"p1\" \"data3\") (\"p2\" \"data4\") (\"p3\" \"data5\")))))\n64\n;; (kgn-text-ui::prompt-selection-list\n65\n;;\n(get-entity-data-helper \"Bill Gates went to Seattle to Microsoft\"))\n66\n67\n(defun colorize-sparql (str &key (stream t))\n68\n\" this could be used to colorize text (as it is in kgn-capi-ui example)\"\n69\n;;(declare (ignore message-stream))\n70\n(declare (ignore stream))\n71\n(format t \"~%~S~%\" str))\n72\n73\n(defun get-string-from-user (text-prompt)\n74\n(format t \"~%~S:~%\" text-prompt)\n75\n(read-line))\n76\n77\n78\n;; Main funtion\n79\n80\n(defun kgn-text-ui ()\n81\n(let (prompt\n82\n(message-stream t)\n83\n(results-stream t))\n84\n(loop\n85\nwhile\n86\n(>\n\nKnowledge Graph Navigator Text-Based User Interface\n244\n87\n(length\n88\n(setf prompt\n89\n(get-string-from-user\n90\n\"Enter entity names (people, places, companies, etc.\")))\n91\n0)\n92\ndo\n93\n(let* ((entity-data (get-entity-data-helper prompt :message-stream t)))\n94\n(let ((user-selections (prompt-selection-list entity-data)))\n95\n(dolist (ev user-selections)\n96\n(if (> (length (cadr ev)) 0)\n97\n(let ()\n98\n(terpri results-stream)\n99\n(format results-stream \"- - - ENTITY TYPE: ~A - - -\" (car ev))\n100\n;;(terpri results-stream)\n101\n(dolist (uri (cadr ev))\n102\n(setf uri (assoc :s (car uri)))\n103\n(case (car ev)\n104\n(:people\n105\n(pprint-results\n106\n(kgn-common:dbpedia-get-person-detail\n107\nuri\n108\n:message-stream message-stream\n109\n:colorize-sparql-function #'colorize-sparql)))\n110\n(:companies\n111\n(pprint-results\n112\n(kgn-common:dbpedia-get-company-detail uri\n113\n:message-stream message-stream\n114\n:colorize-sparql-function #'colorize-sparql)))\n115\n(:countries\n116\n(pprint-results\n117\n(kgn-common:dbpedia-get-country-detail uri\n118\n:message-stream message-stream\n119\n:colorize-sparql-function #'colorize-sparql)))\n120\n(:cities\n121\n(pprint-results\n122\n(kgn-common:dbpedia-get-city-detail\nuri\n123\n:message-stream message-stream\n124\n:colorize-sparql-function #'colorize-sparql)))\n125\n(:products\n126\n(pprint-results\n127\n(kgn-common:dbpedia-get-product-detail uri\n128\n:message-stream message-stream\n129\n:colorize-sparql-function #'colorize-sparql)))))))))))))\n\nKnowledge Graph Navigator Text-Based User Interface\n245\n130\n131\n;; (kgn-text-ui:kgn-text-ui)\nThe utility function multiple-selections listed in lines 10-29 displays a list of user choices, adding a\nzero-based index for each list item. The user can enter zero or more indices to indicate their choices\nusing the function prompt-selection-list listed in lines 35-59.\nThe commented out code in lines 61-65 can be used to test these two functions.\nThe main function kgn-text-ui is listed in lines 80-129.\nWrap-up\nIn the previous chapter we implemented the Knowledge Graph Navigator library. Here we developed\na text-based user interface. In the next chapter we use the library to develop a LispWorks specific\nCAPI user interface.\n\nKnowledge Graph Navigator User\nInterface Using LispWorks CAPI\nAs we have seen in the last two chapters the Knowledge Graph Navigator (which I will often refer\nto as KGN) is a tool for processing a set of entity names and automatically exploring the public\nKnowledge Graph DBPedia⁹⁵ using SPARQL queries. I started to write KGN for my own use, to\nautomate some things I used to do manually when exploring Knowledge Graphs, and later thought\nthat KGN might also be useful for educational purposes. KGN shows the user the auto-generated\nSPARQL queries so hopefully the user will learn by seeing examples. KGN uses NLP code developed\nin earlier chapters and we will reuse that code with a short review of using the APIs. Here is a\nscreenshot showing the application we develop here:\nUI for the Knowledge Graph Navigator\nWe will use the KGN common library developed earlier. This example replaces the text bases UI\nfrom the last chapter and requires either the free or professional version of LispWorks to run.\nThe code for the CAPI user interface is found in the GitHub repository https://github.com/mark-\nwatson/kgn-capi-ui⁹⁶.\n⁹⁵http://dbpedia.org\n⁹⁶https://github.com/mark-watson/kgn-capi-ui\n\nKnowledge Graph Navigator User Interface Using LispWorks CAPI\n247\nProject Configuration and Running the Application\nThe following listing of kgn.asd shows the five packages this example depends on in addition to\n#:kgn-common that was developed in an earlier chapter that is referenced in the file package.lisp:\n1\n;;;; knowledgegraphnavigator.asd\n2\n3\n(asdf:defsystem #:kgn-capi-ui\n4\n:description \"top level Knowledge Graph Navigator package\"\n5\n:author \"Mark Watson <markw@markwatson.com>\"\n6\n:license \"Apache 2\"\n7\n:depends-on (#:kgn-common #:sparql #:kbnlp #:lw-grapher #:trivial-open-browser)\n8\n:components ((:file \"package\")\n9\n(:file \"kgn-capi-ui\")\n10\n(:file \"option-pane\")\n11\n(:file \"colorize\")\n12\n(:file \"user-interface\")))\nOther dependency libraries specified in project.lisp are trivial-open-browser which we will use\nto open a web browser to URIs for human readable information on DBPedia and sparql that was\ndeveloped in an earlier chapter.\nListing of package.lisp:\n1\n;;;; package.lisp\n2\n3\n(defpackage #:kgn-capi-ui\n4\n(:use #:cl)\n5\n(:use #:kgn-common #:sparql #:lw-grapher #:trivial-open-browser)\n6\n(:export #:kgn-capi-ui))\nThe free personal edition of LispWorks does not support initialization files so you must manually\nload Quicklisp from the Listener Window when you first start LispWorks Personal as seen in the\nfollowing repl listing (edited to remove some output for brevity). Once Quicklisp is loaded we then\nuse ql:quickload to load the example in this chapter (some output removed for brevity):\n\nKnowledge Graph Navigator User Interface Using LispWorks CAPI\n248\nCL-USER 1 > (load \"~/quicklisp/setup.lisp\")\n; Loading text file /Users/markw/quicklisp/setup.lisp\n; Loading /Applications/LispWorks Personal 7.1/...\n;; Creating system \"COMM\"\n#P\"/Users/markw/quicklisp/setup.lisp\"\nCL-USER 2 > (ql:quickload \"kgn\")\nTo load \"kgn\":\nLoad 1 ASDF system:\nkgn\n; Loading \"kgn\"\n.\n\"Starting to load data....\"\n\"....done loading data.\"\n\"#P\\\"/Users/markw/GITHUB/common-lisp/entity-uris/entity-uris.lisp\\\"\"\n\"current directory:\"\n\"/Users/markw/GITHUB/common-lisp/entity-uris\"\n\"Starting to load data....\"\n\"....done loading data.\"\n[package kgn]\nTo load \"sqlite\":\nLoad 1 ASDF system:\nsqlite\n; Loading \"sqlite\"\nTo load \"cl-json\":\nLoad 1 ASDF system:\ncl-json\n; Loading \"cl-json\"\nTo load \"drakma\":\nLoad 1 ASDF system:\ndrakma\n; Loading \"drakma\"\n.To load \"entity-uris\":\nLoad 1 ASDF system:\nentity-uris\n; Loading \"entity-uris\"\n(\"kgn\")\nCL-USER 3 > (kgn:kgn)\n#<KGN::KGN-INTERFACE \"Knowledge Graph Navigator\" 40201E91DB>\nPlease note that I assume you have configured all of the examples for this book for discoverability\nby Quicklisp as per the section Setup for Local Quicklisp Projects in Appendix A.\nWhen the KGN application starts a sample query is randomly chosen. Queries with many entities\n\nKnowledge Graph Navigator User Interface Using LispWorks CAPI\n249\ncan take a while to process, especially when you first start using this application. Every time KGN\nmakes a web service call to DBPedia the query and response are cached in a SQLite database in\n∼/.kgn_cache.db which can greatly speed up the program, especially in development mode when\ntesting a set of queries. This caching also takes some load off of the public DBPedia endpoint, which\nis a polite thing to do.\nI use LispWorks Professional and add two utility functions to the bottom on my ∼/.lispworks\nconfiguration file (you can’t do this with LispWorks Personal):\n1\n;;; The following lines added by ql:add-to-init-file:\n2\n#-quicklisp\n3\n(let ((quicklisp-init\n4\n(merge-pathnames\n5\n\"quicklisp/setup.lisp\"\n6\n(user-homedir-pathname))))\n7\n(when (probe-file quicklisp-init)\n8\n(load quicklisp-init)))\n9\n10\n(defun ql (x) (ql:quickload x))\n11\n(defun qlp (x)\n12\n(ql:quickload x)\n13\n(SYSTEM::%IN-PACKAGE (string-upcase x) :NEW T))\nFunction ql is just a short alias to avoid frequently typing ql:quickload and qlp loads a Quicklisp\nproject and then performs an in-package of the Common Lisp package with the same name as the\nQuicklisp project.\nUtilities to Colorize SPARQL and Generated Output\nWhen I first had the basic functionality of KGN with a CAPI UI working, I was disappointed by how\nthe application looked as all black text on a white background. Every editor and IDE I use colorizes\ntext in an appropriate way so I took advantage of the function capi::write-string-with-properties\nto easily implement color hilting SPARQL queries.\nThe code in the following listing is in the file kgn/colorize.lisp. When I generate SPARQL queries\nto show the user I use the characters “@@” as placeholders for end of lines in the generated output.\nIn line 5 I am ensuring that there are spaces around these characters so they get tokenized properly.\nIn the loop starting at line 7 I process the tokens checking each one to see if it should have a color\nassociated with it when it is written to the output stream.\n\nKnowledge Graph Navigator User Interface Using LispWorks CAPI\n250\n1\n(in-package #:kgn)\n2\n3\n(defun colorize-sparql (s\n&key (stream nil))\n4\n(let ((tokens (tokenize-string-keep-uri\n5\n(replace-all s \"@@\" \" @@ \")))\n6\nin-var)\n7\n(dolist (token tokens)\n8\n(if (> (length token) 0)\n9\n(if (or in-var (equal token \"?\"))\n10\n(capi::write-string-with-properties\n11\ntoken\n12\n'(:highlight :compiler-warning-highlight)\n13\nstream)\n14\n(if (find token '(\"where\" \"select\" \"distinct\" \"option\" \"filter\"\n15\n\"FILTER\" \"OPTION\" \"DISTINCT\"\n16\n\"SELECT\" \"WHERE\")\n17\n:test #'equal)\n18\n(capi::write-string-with-properties\n19\ntoken\n20\n'(:highlight :compiler-note-highlight)\n21\nstream)\n22\n(if (equal (subseq token 0 1) \"<\")\n23\n(capi::write-string-with-properties\n24\ntoken\n25\n'(:highlight :bold)\n26\nstream)\n27\n(if (equal token \"@@\")\n28\n(terpri stream)\n29\n(if (not (equal token \"~\")) (write-string token stream)))))))\n30\n(if (equal token \"?\")\n31\n(setf in-var t)\n32\n(setf in-var nil))\n33\n(if (and\n34\n(not in-var)\n35\n(not (equal token \"?\")))\n36\n(write-string \" \" stream)))\n37\n(terpri stream)))\nHere is an example call to function colorize-sparql:\n\nKnowledge Graph Navigator User Interface Using LispWorks CAPI\n251\nKGN 25 > (colorize-sparql \"select ?s ?p\nwhere {@@\n?s ?p \\\"Microsoft\\\" } @@\nFILTER\\\n(lang(?comment) = 'en')\")\nselect ?s ?p where {\n?s ?p \"Microsoft\" }\nFILTER ( lang ( ?comment ) = 'en' )\nMain Implementation File kgn-capi-ui.lisp\n1\n;;----------------------------------------------------------------------------\n2\n;; To try it, compile and load this file and then execute:\n3\n;;\n4\n;;\n(kgn::kgn)\n5\n;;\n6\n;;----------------------------------------------------------------------------\n7\n;; Copyright (c) 2020-2022 Mark Watson. All rights reserved.\n8\n;;----------------------------------------------------------------------------\n9\n10\n(in-package #:kgn-capi-ui)\n11\n12\n(defvar *width* 1370)\n13\n(defvar *best-width* 1020)\n14\n(defvar *show-info-pane* t)\n15\n16\n(defvar *pane2-message*\n17\n\"In order to process your query a series of SPARQL queries will be formed based on\\\n18\nthe query. These generated SPARQL queries will be shown here and the reuslts of the\\\n19\nqueries will be formatted and displayed in the results display pane below.\")\n20\n21\n(defvar *pane3-message*\n22\n\"Enter a query containing entities like people's names, companys, places, etc. fol\\\n23\nlowing by the RETURN key to start processing your query. You can also directly use a\\\n24\nDBPedia URI for an entity, for example: <http://dbpedia.org/resource/Apple_Inc.> Wh\\\n25\nen you start this application, a sample query is randomly chosen to get you started.\\\n26\n\")\n27\n28\n(defun test-callback-click (selected-node-name)\n29\n(ignore-errors\n30\n(format nil \"* user clicked on node: ~A~%\" selected-node-name)))\n31\n32\n(defun test-callback-click-shift (selected-node-name)\n33\n(ignore-errors\n\nKnowledge Graph Navigator User Interface Using LispWorks CAPI\n252\n34\n(if (equal (subseq selected-node-name 0 5) \"<http\")\n35\n(trivial-open-browser:open-browser\n36\n(subseq selected-node-name 1 (- (length selected-node-name) 1))))\n37\n(format nil \"* user shift-clicked on node: ~A - OPEN WEB BROWSER~%\" selected-nod\\\n38\ne-name)))\n39\n40\n(defun cache-callback (&rest x) (declare (ignore x))\n41\n(if *USE-CACHING*\n42\n(capi:display (make-instance 'options-panel-interface))))\n43\n44\n(defun website-callback (&rest x) (declare (ignore x)) (trivial-open-browser:open-br\\\n45\nowser \"http://www.knowledgegraphnavigator.com/\"))\n46\n47\n(defun toggle-grapher-visibility (&rest x)\n48\n(declare (ignore x))\n49\n(setf *show-info-pane* (not *show-info-pane*)))\n50\n51\n(defvar *examples*)\n52\n(setf *examples* '(\"Bill Gates and Melinda Gates at Microsoft in Seattle\"\n53\n\"Bill Clinton <http://dbpedia.org/resource/Georgia_(U.S._state)>\"\n54\n\"Bill Gates and Steve Jobs visited IBM and Microsoft in Berlin, S\\\n55\nan Francisco, Toronto, Canada\"\n56\n\"Steve Jobs lived near San Francisco and was a founder of <http:/\\\n57\n/dbpedia.org/resource/Apple_Inc.>\"\n58\n\"<http://dbpedia.org/resource/Bill_Gates> visited IBM\"\n59\n\"<http://dbpedia.org/resource/Bill_Gates> visited <http://dbpedia\\\n60\n.org/resource/Apple_Inc.>\"\n61\n\"Bill Gates visited <http://dbpedia.org/resource/Apple_Inc.>\"))\n62\n63\n(capi:define-interface kgn-interface ()\n64\n()\n65\n(:menus\n66\n(action-menu\n67\n\"Actions\"\n68\n(\n69\n(\"Copy generated SPARQL to clipboard\"\n70\n:callback\n71\n#'(lambda (&rest x) (declare (ignore x))\n72\n(let ((messages (capi:editor-pane-text text-pane2)))\n73\n(capi::set-clipboard text-pane2 (format nil \"---- Generated SPARQL and c\\\n74\nomments:~%~%~A~%~%\" messages) nil))))\n75\n(\"Copy results to clipboard\"\n76\n:callback\n\nKnowledge Graph Navigator User Interface Using LispWorks CAPI\n253\n77\n#'(lambda (&rest x) (declare (ignore x))\n78\n(let ((results (capi:editor-pane-text text-pane3)))\n79\n(capi::set-clipboard text-pane2 (format nil \"---- Results:~%~%~A~%\" resu\\\n80\nlts) nil))))\n81\n(\"Copy generated SPARQL and results to clipboard\"\n82\n:callback\n83\n#'(lambda (&rest x) (declare (ignore x))\n84\n(let ((messages (capi:editor-pane-text text-pane2))\n85\n(results (capi:editor-pane-text text-pane3)))\n86\n(capi::set-clipboard\n87\ntext-pane2\n88\n(format nil \"---- Generated SPARQL and comments:~%~%~A~%~%---- Results:\\\n89\n~%~%~A~%\" messages results) nil))))\n90\n(\"Visit Knowledge Graph Navigator Web Site\" :callback 'website-callback)\n91\n(\"Clear query cache\" :callback 'cache-callback)\n92\n((if *show-info-pane*\n93\n\"Stop showing Grapher window for new results\"\n94\n\"Start showing Grapher window for new results\")\n95\n:callback 'toggle-grapher-visibility)\n96\n)))\n97\n(:menu-bar action-menu)\n98\n(:panes\n99\n(text-pane1\n100\ncapi:text-input-pane\n101\n:text (nth (random (length *examples*)) *examples*)\n102\n:title \"Query\"\n103\n:min-height 80\n104\n:max-height 100\n105\n:max-width *width*\n106\n;;:min-width (- *width* 480)\n107\n:width *best-width*\n108\n:callback 'start-background-thread)\n109\n110\n(text-pane2\n111\ncapi:collector-pane\n112\n:font \"Courier\"\n113\n:min-height 210\n114\n:max-height 250\n115\n:title \"Generated SPARQL queries to get results\"\n116\n:text \"Note: to answer queries, this app makes multipe SPARQL queries to DBPedia\\\n117\n. These SPARQL queries will be shown here.\"\n118\n:vertical-scroll t\n119\n:create-callback #'(lambda (&rest x)\n\nKnowledge Graph Navigator User Interface Using LispWorks CAPI\n254\n120\n(declare (ignore x))\n121\n(setf (capi:editor-pane-text text-pane2) *pane2-message*))\n122\n:max-width *width*\n123\n:width *best-width*\n124\n:horizontal-scroll t)\n125\n126\n(text-pane3\n127\ncapi:collector-pane ;; capi:display-pane ;; capi:text-input-pane\n128\n:text *pane3-message*\n129\n:font \"Courier\"\n130\n:line-wrap-marker nil\n131\n:wrap-style :split-on-space\n132\n:vertical-scroll :with-bar\n133\n:title \"Results\"\n134\n:horizontal-scroll t\n135\n:min-height 220\n136\n:width *best-width*\n137\n:create-callback #'(lambda (&rest x)\n138\n(declare (ignore x))\n139\n(setf (capi:editor-pane-text text-pane3) *pane3-message*))\n140\n:max-height 240\n141\n:max-width *width*)\n142\n(info\n143\ncapi:title-pane\n144\n:text \"Use natural language queries to generate SPARQL\"))\n145\n(:layouts\n146\n(main-layout\n147\ncapi:grid-layout\n148\n'(nil info\n149\nnil text-pane1\n150\nnil text-pane2\n151\nnil text-pane3)\n152\n:x-ratios '(1 99)\n153\n:has-title-column-p t))\n154\n(:default-initargs\n155\n:layout 'main-layout\n156\n:title \"Knowledge Graph Navigator\"\n157\n:best-width *best-width*\n158\n:max-width *width*))\n159\n160\n(defun start-background-thread (query-text self)\n161\n(format t \"~%** ** entering start-progress-bar-test-from-background-thread:~%~%sel\\\n162\nf=~S~%~%\" self)\n\nKnowledge Graph Navigator User Interface Using LispWorks CAPI\n255\n163\n(with-slots (text-pane2 text-pane3) self\n164\n(print text-pane2)\n165\n(mp:process-run-function \"progress-bar-test-from-background-thread\"\n166\n'()\n167\n'run-and-monitor-progress-background-thread\n168\nquery-text text-pane2 text-pane3)))\n169\n170\n;; This function runs in a separate thread.\n171\n172\n(defun run-and-monitor-progress-background-thread (text text-pane2 text-pane3)\n173\n(unwind-protect\n174\n(setf (capi:editor-pane-text text-pane2) \"\")\n175\n(setf (capi:editor-pane-text text-pane3) \"\")\n176\n;;(capi:display-message \"done\")\n177\n(let ((message-stream (capi:collector-pane-stream text-pane2))\n178\n(results-stream (capi:collector-pane-stream text-pane3)))\n179\n(format message-stream \"# Starting to process query....~%\")\n180\n(format results-stream *pane3-message*)\n181\n(let ((user-selections (prompt-selection-list (get-entity-data-helper text :me\\\n182\nssage-stream message-stream))))\n183\n(print \"***** from prompt selection list:\") (print user-selections)\n184\n(setf (capi:editor-pane-text text-pane3) \"\")\n185\n(dolist (ev user-selections)\n186\n(if (> (length (cadr ev)) 0)\n187\n(let ()\n188\n(terpri results-stream)\n189\n(capi::write-string-with-properties\n190\n(format nil \"- - - ENTITY TYPE: ~A - - -\" (car ev))\n191\n'(:highlight :compiler-error-highlight) results-stream)\n192\n;;(terpri results-stream)\n193\n(dolist (uri (cadr ev))\n194\n(setf uri (car uri))\n195\n(case (car ev)\n196\n(:people\n197\n(pprint-results\n198\n(kgn-common:dbpedia-get-person-detail\nuri :message-stream mes\\\n199\nsage-stream :colorize-sparql-function #'colorize-sparql)\n200\n:stream results-stream))\n201\n(:companies\n202\n(pprint-results\n203\n(kgn-common:dbpedia-get-company-detail uri :message-stream mes\\\n204\nsage-stream :colorize-sparql-function #'colorize-sparql)\n205\n:stream results-stream))\n\nKnowledge Graph Navigator User Interface Using LispWorks CAPI\n256\n206\n(:countries\n207\n(pprint-results\n208\n(kgn-common:dbpedia-get-country-detail uri :message-stream mes\\\n209\nsage-stream :colorize-sparql-function #'colorize-sparql)\n210\n:stream results-stream))\n211\n(:cities\n212\n(pprint-results\n213\n(kgn-common:dbpedia-get-city-detail\nuri :message-stream mes\\\n214\nsage-stream :colorize-sparql-function #'colorize-sparql)\n215\n:stream results-stream))\n216\n(:products\n217\n(pprint-results\n218\n(kgn-common:dbpedia-get-product-detail uri :message-stream mes\\\n219\nsage-stream :colorize-sparql-function #'colorize-sparql)\n220\n:stream results-stream)))))))\n221\n222\n(let (links x)\n223\n(dolist (ev user-selections)\n224\n(dolist (uri (second ev))\n225\n(setf uri (car uri))\n226\n(if (> (length ev) 2)\n227\n(setf x (caddr ev)))\n228\n(setf links (cons (list (symbol-name (first ev)) uri x) links)))\n229\n230\n(setf\n231\nlinks\n232\n(append\n233\nlinks\n234\n(entity-results->relationship-links\n235\nuser-selections\n236\n:message-stream message-stream))))\n237\n238\n(if\n239\n*show-info-pane*\n240\n(lw-grapher:make-info-panel-grapher '(\"PEOPLE\" \"COMPANIES\" \"COUNTRIES\"\\\n241\n\"CITIES\" \"PRODUCTS\" \"PLACES\")\n242\nlinks 'test-callback-click 'test-c\\\n243\nallback-click-shift))) ;; do\nnot use #' !!\n244\n(terpri results-stream)\n245\n(princ \"** Done wih query **\" results-stream)))))\n246\n247\n248\n\nKnowledge Graph Navigator User Interface Using LispWorks CAPI\n257\n249\n;; MAIN entry point for application:\n250\n251\n(defun kgn-capi-ui ()\n252\n;;(ignore-errors (create-dbpedia))\n253\n(capi:display (make-instance 'kgn-interface)))\nUser Interface Utilites File user-interface.lisp\nIn the previous chapter, the function prompt-selection-list was defined in the file kgn-text-ui/kgn-\ntext-ui.lisp for text based (console) UIs. Here it is implemented in a separate file user-interface.lisp\nin the project directory kgn-capi-ui.\n1\n(in-package #:kgn-capi-ui)\n2\n3\n;; (use-package \"CAPI\")\n4\n5\n(defun prompt-selection-list (a-list-of-choices)\n6\n(let (ret)\n7\n(dolist (choice a-list-of-choices)\n8\n(setf choice (remove-if #'null choice))\n9\n(let* ((topic-type (car choice))\n10\n(choice-list-full (rest choice))\n11\n(choice-list (remove-duplicates\n12\n(map 'list #'(lambda (z)\n13\n(list\n14\nz ;; (first z)\n15\n(string-shorten\n16\n(kgn-common:clean-comment\n17\n(kgn-common:clean-comment (cadr z)))\n18\n140 :first-remove-stop-words t)))\n19\n(apply #'append choice-list-full))\n20\n:test #'equal)))\n21\n(let ((dialog-results (alexandria:flatten\n22\n(capi:prompt-with-list ;; SHOW SELECTION LIST\n23\n(map 'list #'second choice-list)\n24\n(symbol-name topic-type)\n25\n:interaction :multiple-selection\n26\n:choice-class 'capi:button-panel\n27\n:pane-args '(:visible-min-width 910\n28\n:layout-class capi:column-layout))))\n29\n(ret2))\n\nKnowledge Graph Navigator User Interface Using LispWorks CAPI\n258\n30\n(dolist (x choice-list)\n31\n(if (find (second x) dialog-results)\n32\n(setf ret2 (cons (car x) ret2))))\n33\n(if (> (length ret2) 0)\n34\n(setf ret (cons (list topic-type (reverse ret2)) ret))))))\n35\n(reverse ret)))\n36\n37\n;; (get-entity-data-helper \"Bill Gates went to Seattle to Microsoft\")\n38\n;; (prompt-selection-list\n39\n;;\n(get-entity-data-helper\n40\n;;\n\"Bill Gates went to Seattle to Microsoft\"))\nUser Interface CAPI Options Panes Definition File\noption-pane.lisp\nIn the following listing we define functions to implement CAPI menus:\n1\n(in-package #:kgn-capi-ui)\n2\n3\n;; options for:\n4\n;;\n1. programming language to generate code snippets for\n5\n;;\n2. colorization options (do we really need this??)\n6\n;;\n3. show disk space used by caching\n7\n;;\n4. option to remove local disk cache\n8\n9\n(defvar *width-options-panel* 800)\n10\n11\n(defun get-cache-disk-space ()\n12\n(let ((x (ignore-errors\n13\n(floor\n14\n(/\n15\n(with-open-file\n16\n(file \"~/Downloads/knowledge_graph_navigator_cache.db\")\n17\n(file-length file)) 1000)))))\n18\n(or x\n0))) ;; units in megabytes\n19\n20\n(defun clear-cache-callback (&rest val)\n21\n(declare (ignore val))\n22\n(ignore-errors (delete-file \"~/Downloads/knowledge_graph_navigator_cache.db\")))\n23\n24\n(defvar *code-snippet-language* nil)\n\nKnowledge Graph Navigator User Interface Using LispWorks CAPI\n259\n25\n(defun set-prog-lang (&rest val)\n26\n(format t \"* set-prog-lang: val=~S~%\" val)\n27\n(setf *code-snippet-language* (first val)))\n28\n29\n(capi:define-interface options-panel-interface ()\n30\n()\n31\n(:panes\n32\n#|\n33\n(prog-lang-pane\n34\ncapi:option-pane\n35\n:items '(\"No language set\" \"Python\" \"Common Lisp\")\n36\n:visible-items-count 6\n37\n:selection (if (equal *code-snippet-language* nil)\n38\n0\n39\n(if (equal *code-snippet-language* \"No language set\")\n40\n0\n41\n(if (equal *code-snippet-language* \"Python\")\n42\n1\n43\n(if (equal *code-snippet-language* \"Common Lisp\")\n44\n2\n45\n0))))\n46\n:interaction :single-selection\n47\n:selection-callback\n48\n'set-prog-lang)|#\n49\n(disk-space-pane\n50\ncapi:text-input-pane\n51\n:text (format nil \"~A (megabytes)\"\n52\n(let ((x\n53\n(ignore-errors\n54\n(floor\n55\n(/\n56\n(with-open-file (file \"~/.kgn_cache.db\")\n57\n(file-length file))\n58\n1000)))))\n59\n(if x\n60\nx\n61\n0)))\n62\n:title \"Current size of cache:\"\n63\n:min-width 170\n64\n:max-width *width-options-panel*)\n65\n(clear-disk-cache-pane\n66\ncapi:push-button-panel\n67\n;;:title \"Clear local query cache:\"\n\nKnowledge Graph Navigator User Interface Using LispWorks CAPI\n260\n68\n:items\n69\n'(\"Clear local query cache\")\n70\n:selection-callback\n71\n#'(lambda (&rest val)\n72\n(declare (ignore val))\n73\n(ignore-errors (delete-file \"~/.kgn_cache.db\"))\n74\n(ignore-errors (setf (capi:text-input-pane-text disk-space-pane)\n75\n\"0 (megabytes)\"))))\n76\n(toggle-graph-display\n77\ncapi:option-pane\n78\n:items '(\"Show Graph Info Pane Browser\" \"Hide Graph Info Pane Browser\")\n79\n:selected-item (if *show-info-pane* 0 1)\n80\n;;:title \"\"\n81\n:selection-callback 'toggle-grapher-visibility))\n82\n83\n(:layouts\n84\n(main-layout\n85\ncapi:grid-layout\n86\n'(nil disk-space-pane\n87\nnil clear-disk-cache-pane)\n88\n:x-ratios '(1 99)\n89\n:has-title-column-p nil))\n90\n(:default-initargs\n91\n:layout 'main-layout\n92\n:title \"Knowledge Graph Navigator Options\"\n93\n:max-width *width-options-panel*))\n94\n95\n;; MAIN entry point for application:\n96\n97\n98\n;;\n(capi:display (make-instance 'options-panel-interface))\n99\n100\n(defun ui2 () (capi:display (make-instance 'options-panel-interface)))\nThe popup list in the last example looks like:\n\nKnowledge Graph Navigator User Interface Using LispWorks CAPI\n261\nPopup list shows the user possible entity resolutions for each entity found in the input query. The user selects the\nresolved entities to use.\nIn this example there were two “Bill Gates” entities, one an early American frontiersman, the other\nthe founder of Microsoft and I chose the latter person to continue finding information about.\nUsing LispWorks CAPI UI Toolkit\nYou can use the free LispWorks Personal Edition for running KGN. Using other Common Lisp\nimplementations like Clozure-CL and SBCL will not work because the CAPI user interface library\nis proprietary to LispWorks. I would like to direct you to three online resources for learning CAPI:\n• [LispWorks’ main web page introducing CAPI⁹⁷\n• LispWorks’ comprehensive CAPI documentation⁹⁸ for LispWorks version 7.1\n• An older web site (last updated in 2011 but I find it useful for ideas): CAPI Cookbook⁹⁹\nI am not going to spend too much time in this chapter explaining my CAPI-based code. If you\nuse LispWorks (either the free Personal or the Professional editions) you are likely to use CAPI\nand spending time on the official documentation and especially the included example programs is\nstrongly recommended.\nIn the next section I will review the KGN specific application parts of the CAPI-based UI.\nThe following figure shows a popup window displaying a graph of discovered entities and\nrelationships:\n⁹⁷http://www.lispworks.com/products/capi.html\n⁹⁸http://www.lispworks.com/products/capi.html\n⁹⁹http://capi.plasticki.com/show?O4\n\nKnowledge Graph Navigator User Interface Using LispWorks CAPI\n262\nUI for info-pane-grapher\nSince I just showed the info-pane-grapher this is a good time to digress to its implementation. This is\nin a different package and you will find the source code in src/lw-grapher/info-pane-grapher.lisp.\nI used the graph layout algorithm from ISI-Grapher Manual (by Gabriel Robbins)¹⁰⁰. There is another\nutility in src/lw-grapher/lw-grapher.lisp that also displays a graph without mouse support and an\nattached information pane that is not used here but you might prefer it for reuse in your projects if\nyou don’t need mouse interactions.\nThe graph nodes are derived from the class capi:pinboard-object:\n1\n(defclass text-node (capi:pinboard-object)\n2\n((text :initarg :text :reader text-node-text)\n3\n(string-x-offset :accessor text-node-string-x-offset)\n4\n(string-y-offset :accessor text-node-string-y-offset)))\nI customized how my graph nodes are drawn in a graph pane (this is derived from LispWorks\nexample code):\n¹⁰⁰http://www.cs.virginia.edu/~robins/papers/The_ISI_Grapher_Manual.pdf\n\nKnowledge Graph Navigator User Interface Using LispWorks CAPI\n263\n1\n(defmethod capi:draw-pinboard-object (pinboard (self text-node)\n2\n&key &allow-other-keys)\n3\n(multiple-value-bind (X Y\nwidth height)\n4\n(capi:static-layout-child-geometry self)\n5\n(let* ((half-width\n(floor (1- width)\n2))\n6\n(half-height (floor (1- height) 2))\n7\n(circle-x (+ X half-width))\n8\n(circle-y (+ Y half-height))\n9\n(background :white)\n10\n(foreground (if background\n11\n:black\n12\n(capi:simple-pane-foreground pinboard)))\n13\n(text (text-node-text self)))\n14\n(gp:draw-ellipse pinboard\n15\ncircle-x circle-y\n16\nhalf-width half-height\n17\n:filled t\n18\n:foreground background)\n19\n(gp:draw-ellipse pinboard\n20\ncircle-x circle-y\n21\nhalf-width half-height\n22\n:foreground foreground)\n23\n(gp:draw-string pinboard\n24\ntext\n25\n(+ X (text-node-string-x-offset self))\n26\n(+ Y (text-node-string-y-offset self))\n27\n:foreground foreground))))\nMost of the work is done in the graph layout method that uses Gabriel Robbins’ algorithm. Here I\njust show the signature and we won’t go into implementation. If you are interested in modifying\nthe layout code, I include a screen shot from ISI-Grapher manual showing the algorithm in a single\npage; see the file src/lw-grapher/Algorithm from ISI-Grapher Manual.png.\nThe following code snippets show the method signature for the layout algorithm function in the file\nsrc/lw-grapher/grapher.lisp. I also include the call to capi:graph-pane-nodes that is the CLOS\nreader method for getting the list of node objects in a graph pane:\n1\n(defun graph-layout (self &key force)\n2\n(declare (ignore force))\n3\n(let* ((nodes (capi:graph-pane-nodes self))\n4\n...\nThe CAPI graph node model uses a function that is passed a node object and returns a list of this\n\nKnowledge Graph Navigator User Interface Using LispWorks CAPI\n264\nnode’s child node objects. There are several examples of this in the CAPI graph examples that are\nincluded with LispWorks (see the CAPI documentation).\nIn src/lw-grapher/lw-grapher.lisp I wrote a function that builds a graph layout and instead of\npassing in a “return children” function I found it more convenient to wrap this process, accepting a\nlist of graph nodes and graph edges as function arguments:\n1\n(in-package :lw-grapher)\n2\n3\n;; A Grapher (using the layout algorithm from the ISI-Grapher\n4\n;; user guide) with an info panel\n5\n6\n(defun make-info-panel-grapher (h-root-name-list h-edge-list\n7\nh-callback-function-click\n8\nh-callback-function-shift-click)\n9\n(let (edges roots last-selected-node node-callback-click\n10\nnode-callback-click-shift output-pane)\n11\n(labels\n12\n((handle-mouse-click-on-pane (pane x y)\n13\n(ignore-errors\n14\n(let ((object (capi:pinboard-object-at-position pane x y)))\n15\n(if object\n16\n(let ()\n17\n(if last-selected-node\n18\n(capi:unhighlight-pinboard-object pane\n19\nlast-selected-node))\n20\n(setf last-selected-node object)\n21\n(capi:highlight-pinboard-object pane object)\n22\n(let ((c-stream (collector-pane-stream output-pane)))\n23\n(format c-stream\n24\n(funcall node-callback-click\n25\n(text-node-full-text object)))\n26\n(terpri c-stream)))))))\n27\n(handle-mouse-click-shift-on-pane (pane x y)\n28\n(ignore-errors\n29\n(let ((object\n30\n(capi:pinboard-object-at-position pane x y)))\n31\n(if object\n32\n(let ()\n33\n(if last-selected-node\n34\n(capi:unhighlight-pinboard-object\n35\npane last-selected-node))\n36\n(setf last-selected-node object)\n\nKnowledge Graph Navigator User Interface Using LispWorks CAPI\n265\n37\n(capi:highlight-pinboard-object pane object)\n38\n(let ((c-stream\n39\n(collector-pane-stream output-pane)))\n40\n(format c-stream\n41\n(funcall node-callback-click-shift\n42\n(text-node-full-text object)))\n43\n(terpri c-stream)))))))\n44\n45\n(info-panel-node-children-helper (node-text)\n46\n(let (ret)\n47\n(dolist (e edges)\n48\n(if (equal (first e) node-text)\n49\n(setf ret (cons (second e) ret))))\n50\n(reverse ret)))\n51\n52\n(make-info-panel-grapher-helper\n53\n(root-name-list edge-list callback-function-click\n54\ncallback-function-click-shift)\n55\n;; example: root-name-list: '(\"n1\") edge-list:\n56\n;;\n'((\"n1\" \"n2\") (\"n1\" \"n3\"))\n57\n(setf edges edge-list\n58\nroots root-name-list\n59\nnode-callback-click callback-function-click\n60\nnode-callback-click-shift callback-function-click-shift)\n61\n(capi:contain\n62\n63\n(make-instance\n64\n'column-layout\n65\n:title \"Entity Browser\"\n66\n:description\n67\n(list\n68\n(make-instance 'capi:graph-pane\n69\n:min-height 330\n70\n:max-height 420\n71\n:roots roots\n72\n:layout-function 'graph-layout\n73\n:children-function #'info-panel-node-children-helper\n74\n:edge-pane-function\n75\n#'(lambda(self from to)\n76\n(declare (ignore self))\n77\n(let ((prop-name \"\"))\n78\n(dolist (edge edge-list)\n79\n(if (and\n\nKnowledge Graph Navigator User Interface Using LispWorks CAPI\n266\n80\n(equal from (first edge))\n81\n(equal to (second edge)))\n82\n(if (and (> (length edge) 2) (third edge))\n83\n(let ((last-index\n84\n(search\n85\n\"/\" (third edge)\n86\n:from-end t)))\n87\n(if last-index\n88\n(setf prop-name\n89\n(subseq (third edge)\n90\n(1+ last-index)))\n91\n(setf prop-name (third edge)))))))\n92\n(make-instance\n93\n'capi:labelled-arrow-pinboard-object\n94\n:data (format nil \"~A\" prop-name))))\n95\n:node-pinboard-class 'text-node\n96\n:input-model `(((:button-1 :release)\n97\n,#'(lambda (pane x y)\n98\n(handle-mouse-click-on-pane\n99\npane x y)))\n100\n((:button-1 :release :shift) ;; :press)\n101\n,#'(lambda (pane x y)\n102\n(handle-mouse-click-shift-on-pane\n103\npane x y))))\n104\n:node-pane-function 'make-text-node)\n105\n(setf\n106\noutput-pane\n107\n(make-instance 'capi:collector-pane\n108\n:min-height 130\n109\n:max-height 220\n110\n:title \"Message collection pane\"\n111\n:text \"...\"\n112\n:vertical-scroll t\n113\n:horizontal-scroll t))))\n114\n:title\n115\n\"Info Pane Browser: mouse click for info, mouse click + shift for web browser\"\n116\n117\n:best-width 550 :best-height 450)))\n118\n(make-info-panel-grapher-helper h-root-name-list\n119\nh-edge-list h-callback-function-click\n120\nh-callback-function-shift-click))))\n\nKnowledge Graph Navigator User Interface Using LispWorks CAPI\n267\nWrap-up\nThis is a long example application for a book so I did not discuss all of the code in the project. If you\nenjoy running and experimenting with this example and want to modify it for your own projects\nthen I hope that I provided a sufficient road map for you to do so.\nI got the idea for the KGN application because I was spending quite a bit of time manually setting up\nSPARQL queries for DBPedia (and other public sources like WikiData) and I wanted to experiment\nwith partially automating this process. I wrote the CAPI user interface for fun since this example\napplication could have had similar functionality as a command line tool.\n\nUsing the OpenAI APIs\nI have been working as an artificial intelligence practitioner since 1982 and the capability of the beta\nOpenAI APIs is the most impressive thing that I have seen in my career so far. These APIs use the\nGPT-3 model.\nI recommend reading the online documentation for the online documentation for the APIs¹⁰¹ to see\nall the capabilities of the beta OpenAI APIs. Let’s start by jumping into the example code. As seen in\nthe package.lisp file we use the UIOP and cl-json libraries and we export three top level functions:\n1\n;;;; package.lisp\n2\n3\n(defpackage #:openai\n4\n(:use #:cl #:uiop #:cl-json)\n5\n(:export #:completions #:summarize #:answer-question))\nThe library that I wrote for this chapter supports three functions that are exported from the\npackage openai: for completing text, summarizing text, and answering general questions. The single\nOpenAI model that the beta OpenAI APIs use is fairly general purpose and can generate cooking\ndirections when given an ingredient list, grammar correction, write an advertisement from a product\ndescription, generate spreadsheet data from data descriptions in English text, etc.\nGiven the examples from https://beta.openai.com¹⁰² and the Common Lisp examples here, you\nshould be able to modify my example code to use any of the functionality that OpenAI documents.\n1\n;;;; openai.asd\n2\n3\n(asdf:defsystem #:openai\n4\n:description \"Library for using the beta OpenAI APIs\"\n5\n:author \"Mark Watson\"\n6\n:license \"Apache 2\"\n7\n:depends-on (#:uiop #:cl-json)\n8\n:components ((:file \"package\")\n9\n(:file \"openai\")))\nWe will look closely at the function completions and then just look at the small differences to the\nother two example functions. The definitions for all three exported functions are kept in the file\nopenai.lisp. You need to request an API key (I had to wait a few weeks to recieve my key) and set\nthe value of the environment variable OPENAI_KEY to your key. You can add a statement like:\n¹⁰¹https://beta.openai.com/docs/introduction/key-concepts\n¹⁰²https://beta.openai.com\n\nUsing the OpenAI APIs\n269\nexport OPENAI_KEY=sa-hdffds7&dhdhsdgffd\nto your .profile or other shell resource file.\nWhile I sometimes use pure Common Lisp libraries to make HTTP requests, I prefer running the\ncurl utility as a separate process for these reasons:\n• No problems with system specific dependencies.\n• Use the standard library UIOP to run a shell command and capture the output as a string.\n• I use curl from the command line when experimenting with web services. After I get working\ncurl options, it is very easy to translate this into Common Lisp code.\nAn example curl command line call to the beta OpenAI APIs is:\n1\ncurl \\\n2\nhttps://api.openai.com/v1/engines/davinci/completions \\\n3\n-H \"Content-Type: application/json\"\n4\n-H \"Authorization: Bearer sa-hdffds7&dhdhsdgffd\" \\\n5\n-d '{\"prompt\": \"The President went to Congress\", \\\n6\n\"max_tokens\": 22}'\nHere the API token “sa-hdffds7&dhdhsdgffd” on line 4 is made up - that is not my API token. All\nof the OpenAI APIs expect JSON data with query parameters. To use the completion API, we set\nvalues for prompt and max_tokens. The value of max_tokens is the requested number of returns\nwords or tokens. We will look at several examples later.\nIn the file openai.lisp we start with a helper function openai-helper that takes a string with the\nOpenAI API call arguments encoded as a curl command, calls the service, and then extracts the\nresults from the returned JSON data:\n1\n(defun openai-helper (curl-command)\n2\n(let ((response\n3\n(uiop:run-program\n4\ncurl-command\n5\n:output :string)))\n6\n(with-input-from-string\n7\n(s response)\n8\n(let* ((json-as-list (json:decode-json s)))\n9\n;; extract text (this might change if OpenAI changes JSON return format):\n10\n(cdar (cadr (nth 4 json-as-list)))))))\nI convert JSON data to a Lisp list in line 8 and in line 10 I reach into the nested results list for\nthe generated text string. You might want to add a debug printout statement to see the value of\njson-as-list.\n\nUsing the OpenAI APIs\n270\nThe three example functions all use this openai-helper function. The first example function\ncompletions sets the parameters to complete a text fragment. You have probably seen examples\nof the OpenAI GPT-3 model writing stories, given a starting sentence. We are using the same model\nand functionality here:\n1\n(defun completions (starter-text max-tokens)\n2\n(let* ((curl-command\n3\n(concatenate\n4\n'string\n5\n\"curl \" open-api-davinci-model-host\n6\n\" -H \\\"Content-Type: application/json\\\"\"\n7\n\" -H \\\"Authorization: Bearer \" (uiop:getenv \"OPENAI_KEY\") \"\\\" \"\n8\n\" -d '{\\\"prompt\\\": \\\"\" starter-text \"\\\", \\\"max_tokens\\\": \"\n9\n(write-to-string max-tokens)\n\"}'\")))\n10\n(openai-helper curl-command)))\nNote that the OpenAI API models are stochastic. When generating output words (or tokens),\nthe model assigns probabilities to possible words to generate and samples a word using these\nprobabilities. As a simple example, suppose given prompt text “it fell and”, then the model could\nonly generate three words, with probabilities for each word based on this prompt text:\n• the 0.9\n• that 0.1\n• a 0.1\nThe model would emit the word the 90% of the time, the word that 10% of the time, or the word\na 10% of the time. As a result, the model can generate different completion text for the same text\nprompt. Let’s look at some examples. We request 22 output tokens (words or punctuation) in the\nfirst two examples and 100 tokens in the third example:\n1\ncl-user> (openai:completions \"The President went to Congress\" 22)\n2\n\" yesterday and proposed a single tax rate for all corporate taxpayers, which he env\\\n3\nisions will be lower than what our\"\n4\n5\ncl-user> (openai:completions \"The President went to Congress\" 22)\n6\n\" last month, asking for authorization of a program, which had previously been appro\\\n7\nved by the Foreign Intelligence Surveillance court as\"\n8\n9\ncl-user> (openai:completions \"The President went to Congress\" 100)\n10\n\" worried about what the massive unpopular bill would do to his low approvals. Democ\\\n11\nrats lost almost every situation to discuss any legislation about this controversial\\\n12\nsubject. Even more so, President Obama failed and had to watch himself be attacked \\\n\nUsing the OpenAI APIs\n271\n13\nby his own party for not leading.\n14\n15\nThere were also two celebrated (in DC) pieces of student loan legislation, which aim\\\n16\ned to make college cheaper. Harkin teamed up with Congressman Roddenbery on one, Stu\\\n17\ndent Loan Affordability Act, and Senator Jack Reed (D\"\n18\ncl-user>\nThe function summarize is very similar to the function completions except the JSON data passed\nto the API has a few additional parameters that let the API know that we want a text summary:\n• presence_penalty - penalize words found in the original text (we set this to zero)\n• temperature - higher values the randomness used to select output tokens. If you set this to zero,\nthen the same prompt text will always yield the same results (I never use a zero value).\n• top_p - also affects randomness. All examples I have seen use a value of 1.\n• frequency_penalty - penalize using the same words repeatedly (I usually set this to zero, but\nyou should experiment with different values)\nWhen summarizing text, try varying the number of generated tokens to get shorter or longer\nsummaries; in the following two examples we ask for 15 output tokens and 50 output tokens:\n1\n(defvar s \"Jupiter is the fifth planet from the Sun and the largest in the Solar Sy\\\n2\nstem. It is a gas giant with a mass one-thousandth that of the Sun, but two-and-a-ha\\\n3\nlf times that of all the other planets in the Solar System combined. Jupiter is one \\\n4\nof the brightest objects visible to the naked eye in the night sky, and has been kno\\\n5\nwn to ancient civilizations since before recorded history. It is named after the Rom\\\n6\nan god Jupiter.[19] When viewed from Earth, Jupiter can be bright enough for its ref\\\n7\nlected light to cast visible shadows,[20] and is on average the third-brightest natu\\\n8\nral object in the night sky after the Moon and Venus.\")\n9\n10\ncl-user> (openai:summarize s 15)\n11\n\"Jupiter is a gas giant because it is primarily composed of hydrogen\"\n12\n13\ncl-user> (openai:summarize s 50)\n14\n\"Jupiter is a gas giant because it is predominantly composed of hydrogen and helium;\\\n15\nit has a solid core that is composed of heavier elements. It is the largest of the \\\n16\nfour giant planets in the Solar System and the largest in the Solar System\"\nThe function answer-question is very similar to the function summarize except the JSON data\npassed to the API has one additional parameter that let the API know that we want a question\nanswered:\n• stop - The OpenAI API examples use the value: [\\n], which is what I use here.\nAdditionally, the model returns a series of answers with the string “nQ:” acting as a delimiter\nbetween the answers.\n\nUsing the OpenAI APIs\n272\n1\n(let ((index (search \"nQ:\" answer)))\n2\n(if index\n3\n(string-trim \" \" (subseq answer 0 index))\n4\n(string-trim \" \" answer)))\nI strongly urge you to add a debug printout to the question answering code to print the full answer\nbefore we check for the delimiter string. For some questions, the OpenAI APIs generate a series of\nanswers that increase in generality. In the example code we just take the most specific answer.\nLet’s look at a few question answering examples and we will discuss possible problems and\nworkarounds:\n1\ncl-user> (openai:answer-question \"Where is the Valley of Kings?\" 60)\n2\n\"It's in Egypt.\"\nLet’s explore some issues with the question answering model. In the last example there is one good\nanswer and the model works well. The next example “What rivers are in Arizona?” shows some\nproblems because there are many rivers in Arizona. Sometimes the model misses a few rivers and\noften river names are repeated in the output. You also don’t necessarily get the same answer for the\nsame input arguments. Here are three examples requesting 70, 90, and 160 output tokens:\n1\ncl-user> (openai:answer-question \"What rivers are in Arizona?\" 70)\n2\n\"The Colorado River, the Gila River, the Little Colorado River, the Salt River, the \\\n3\nVerde River, the San Pedro River, the Santa Cruz River, the San Juan River, the Agua\\\n4\nFria River, the Hassayampa River, the Bill Williams River, the Little Colorado Rive\\\n5\nr, the San Francisco River, the San Pedro River\"\n6\n7\ncl-user> (openai:answer-question \"What rivers are in Arizona?\" 90)\n8\n\"The Colorado River, the Gila River, the Little Colorado River, the Salt River, the \\\n9\nVerde River, the San Pedro River, the Santa Cruz River, the San Juan River, the Agua\\\n10\nFria River, the Hassayampa River, the Bill Williams River, the Little Colorado Rive\\\n11\nr, the San Francisco River, the San Pedro River, the Santa Cruz River, the San Juan \\\n12\nRiver, the Agua Fria River, the Hass\"\n13\ncl-user> (openai:answer-question \"What rivers are in Arizona?\" 160)\n14\n\"Colorado, Gila, Salt, Verde, and the Little Colorado.\"\n15\n16\ncl-user> (openai:answer-question \"What rivers are in Arizona?\" 160)\n17\n\"The Colorado River, the Gila River, the Little Colorado River, the Salt River, the \\\n18\nVerde River, the San Pedro River, the Santa Cruz River, the San Juan River, the Agua\\\n19\nFria River, the Hassayampa River, the Bill Williams River, the Little Colorado Rive\\\n20\nr, the San Francisco River, the San Pedro River, the Santa Cruz River, the San Juan \\\n21\nRiver, the Agua Fria River, the Hassayampa River, the Bill Williams River, the Littl\\\n22\ne Colorado River, the San Francisco River, the San Pedro River, the Santa Cruz River\\\n\nUsing the OpenAI APIs\n273\n23\n, the San Juan River, the Agua Fria River, the Hassayampa River, the Bill Williams R\\\n24\niver, the Little Colorado River, the San Francisco River, the San Pedro River, the S\\\n25\nanta Cruz\"\nMy library does not handle embedded single quote characters in questions so the question “Who is\nBill Clinton’s wife?” will throw an error. Leaving out the single quote character works fine:\n1\ncl-user> (openai:answer-question \"Who is Bill Clintons wife?\" 120)\n2\n\"Hillary Clinton.\"\n3\ncl-user>\nIn addition to reading the beta OpenAI API documentation you might want to read general material\non the use of OpenAI’s GPT-3 model. Since the APIs we are using are beta they may change. I will\nupdate this chapter and the source code on GitHub if the APIs change.\n\nUsing the Hugging Face Deep\nLearning Natural Language\nProcessing APIs\nTBD: this chapter is currently being written and will be completed along with other manuscript\nchanges in Mark 2023.\nAccessing the HuggingFace NLP APIs is similar to the code we used previously to access the OpenAI\nAPIs.\n1\n(in-package #:huggingface)\n2\n3\n;; define the environment variable \"HF_API_TOKEN\" with the value of your Hugging Fac\\\n4\ne API key\n5\n6\n(defun huggingface-helper (curl-command)\n7\n(let ((response\n8\n(uiop:run-program\n9\ncurl-command\n10\n:output :string)))\n11\n(with-input-from-string\n12\n(s response)\n13\n(let* ((json-as-list (json:decode-json s)))\n14\njson-as-list))))\n15\n16\n(defun summarize (some-text max-tokens)\n17\n(let* ((curl-command\n18\n(concatenate\n19\n'string\n20\n\"curl https://api-inference.huggingface.co/models/facebook/bart-large-cnn\"\n21\n\" -H \\\"Content-Type: application/json\\\"\"\n22\n\" -H \\\"Authorization: Bearer \" (uiop:getenv \"HF_API_TOKEN\") \"\\\" \"\n23\n\" -d '{\\\"inputs\\\": \\\"\" some-text \"\\\", \\\"max_length\\\": \"\n24\n(write-to-string max-tokens) \" }'\")))\n25\n(cdaar (huggingface-helper curl-command))))\n26\n27\n(defun answer-question (question-text context-text)\n28\n(let* ((curl-command\n\nUsing the Hugging Face Deep Learning Natural Language Processing APIs\n275\n29\n(concatenate\n30\n'string\n31\n\"curl https://api-inference.huggingface.co/models/deepset/roberta-base-sq\\\n32\nuad2\"\n33\n\" -H \\\"Content-Type: application/json\\\"\"\n34\n\" -H \\\"Authorization: Bearer \" (uiop:getenv \"HF_API_TOKEN\") \"\\\" \"\n35\n\" -d '{\\\"question\\\": \\\"\" question-text \"\\\", \\\"context\\\": \\\"\"\n36\ncontext-text \"\\\" }'\"))\n37\n(answer (huggingface-helper curl-command)))\n38\n(cdar (last answer))))\n1\nCL-USER>\n(ql:quickload :huggingface)\n2\nTo load \"huggingface\":\n3\nLoad 1 ASDF system:\n4\nhuggingface\n5\n; Loading \"huggingface\"\n6\n7\n(:HUGGINGFACE)\n8\nCL-USER> (huggingface:summarize \"Jupiter is the fifth planet from the Sun and the la\\\n9\nrgest in the Solar System. It is a gas giant with a mass one-thousandth that of the \\\n10\nSun, but two-and-a-half times that of all the other planets in the Solar System comb\\\n11\nined. Jupiter is one of the brightest objects visible to the naked eye in the night \\\n12\nsky, and has been known to ancient civilizations since before recorded history. It i\\\n13\ns named after the Roman god Jupiter.[19] When viewed from Earth, Jupiter can be brig\\\n14\nht enough for its reflected light to cast visible shadows,[20] and is on average the\\\n15\nthird-brightest natural object in the night sky after the Moon and Venus.\" 30)\n16\n\"Jupiter is the fifth planet from the Sun and the largest in the Solar System. When \\\n17\nviewed from Earth, Jupiter can be bright enough for its reflected light to cast visi\\\n18\nble shadows. It is on average the third-brightest natural object in the night sky af\\\n19\nter the Moon and Venus. It has been known to ancient civilizations since before reco\\\n20\nrded history.\"\n21\n22\n\"Jupiter is the fifth planet from the Sun and the largest in the Solar System. When \\\n23\nviewed from Earth, Jupiter can be bright enough for its reflected light to cast visi\\\n24\nble shadows. It is on average the third-brightest natural object in the night sky af\\\n25\nter the Moon and Venus. It has been known to ancient civilizations since before reco\\\n26\nrded history.\"\n27\n28\nCL-USER> (huggingface:answer-question \"Where were the 1992 Olympics held?\" \"The 1992\\\n29\nSummer Games were the first since the end of the Cold War, and the first unaffected\\\n30\nby boycotts since the 1972 Summer Games. The 1992 Olympics were in Greece. 1992 was\\\n31\nalso the first year South Africa was re-invited to the Olympic Games by the Interna\\\n\nUsing the Hugging Face Deep Learning Natural Language Processing APIs\n276\n32\ntional Olympic Committee, after a 32-year ban from participating in international sp\\\n33\nort.\")\n34\n35\n\"Greece\"\n36\nCL-USER>\n1\n\nUsing a Local Document Embeddings\nVector Database With OpenAI GPT3\nAPIs for Semantically Querying Your\nOwn Data\nThis project is inspired by the Python LangChain and LlamaIndex projects, with just the parts I\nneed for my projects written from scratch in Common Lisp. I wrote a Python book “LangChain and\nLlamaIndex Projects Lab Book: Hooking Large Language Models Up to the Real World Using GPT-3,\nChatGPT, and Hugging Face Models in Applications” in March 2023: https://leanpub.com/langchain¹⁰³\nthat you might also be interested in.\nThe GitHub repository for this example can be found here: https://github.com/mark-watson/docs-\nqa¹⁰⁴. This code also requires my OpenAI Common Lisp library https://github.com/mark-wat-\nson/openai¹⁰⁵.\nOverview of Local Embeddings Vector Database to\nEnhance the Use of GPT3 APIs With Local Documents\nIn this example we will use the SqLite database to store the text from documents as well as\nOpenAI embedding vectors for the text. Each embedding vector is 1536 floating point numbers.\nTwo documents are semantically similar if the dot product of their embedding vectors is large.\nFor long documents, we extract the text and create multiple chunks of text. Each chunk is stored\nas a row in a SqLite database table. This is an easy way to implement a vector datastore. There are\nmany open source and commercial vector datastores if you reach performance limits with the simple\ntechniques we use here.\nFor each text chunk we call an OpenAI API to get an embedding vector. Later when we want to\nhave a GPT enabled conversation or just semantically query our local documents, we take the user’s\nquery and call an OpenAI API to get an embedding vector for the query text. We then compute\nthe vector dot product between the query embedding vector and each chunk embedding vector. We\nsave the text of the chunks that are semantically similar to the query embedding vector and use this\ntext as “context text” that we pass to an OpenAI Large Language Model (LLM) API along with the\nuser’s original query text.\n¹⁰³https://leanpub.com/langchain\n¹⁰⁴https://github.com/mark-watson/docs-qa\n¹⁰⁵https://github.com/mark-watson/openai\n\nUsing a Local Document Embeddings Vector Database With OpenAI GPT3 APIs for Semantically Querying Your Own Data\n278\nWhat does this process really do? Normally when you query ChatGPT or similar LLMs, we are\nquerying against knowledge gained from all the original model training text. This process can lead\nto so-called “model hallucinations” where the model “makes stuff up.” The advantage to the using the\nPython libraries LangChain and LlamaIndex is that a LLM is effectively using all original training\ndata but is also primed with hopefully relevant context text from your local documents that might\nbe useful for answering the user’s query. We will replicate a small amount of this functionality in\nCommon Lisp.\nAt the end of this chapter we will extend our code for single queries with a conversational example.\nOur approach to this is simple: when we pass context text and a query, we also pass previous\nconversational queries from the user. I am still experimenting with the ideas in this chapter so please\ndo occasionally look for updates to the GitHub repository https://github.com/mark-watson/docs-\nqa¹⁰⁶ and updates to this book.\nImplementing a Local Vector Database for Document\nEmbeddings\nIn the following listing of the file docs-qa.lisp we start in lines 6-31 with a few string utility functions\nwe will need: write-floats-to-string, read-file, concat-strings, truncate-string, and break-into-\nchunks.\nThe function break-into-chunks is a work in progress. For now we simply cut long input texts into\nspecific chunk lengths, often cutting words in half. A future improvement will be detecting sentence\nboundaries and breaking text on sentences. The Python libraries LangChain and LlamaIndex have\nmultiple chunking strategies.\nIn lines 33-37 function decode-row takes data from a SQL query to fetch a database table row and\nextracts the original chunk text and the embedding vector. Because of the overhead of making many\ncalls to the OpenAI APIs the time spent running the local Common Lisp example code is very small\nso I have not yet worked on making my code efficient.\n1\n(ql:quickload :sqlite)\n2\n(use-package :sqlite)\n3\n4\n;; define the environment variable \"OPENAI_KEY\" with the value of your OpenAI API key\n5\n6\n(defun write-floats-to-string (lst)\n7\n(with-output-to-string (out)\n8\n(format out \"( \")\n9\n(loop for i in lst\n10\ndo (format out \"~f \" i))\n11\n(format out \" )\")))\n¹⁰⁶https://github.com/mark-watson/docs-qa\n\nUsing a Local Document Embeddings Vector Database With OpenAI GPT3 APIs for Semantically Querying Your Own Data\n279\n12\n13\n(defun read-file (infile) ;; from Bing+ChatGPT\n14\n(with-open-file (instream infile\n15\n:direction :input\n16\n:if-does-not-exist nil)\n17\n(when instream\n18\n(let ((string (make-string (file-length instream))))\n19\n(read-sequence string instream)\n20\nstring))))\n21\n22\n(defun concat-strings (list)\n23\n(apply #'concatenate 'string list))\n24\n25\n(defun truncate-string (string length)\n26\n(subseq string 0 (min length (length string))))\n27\n28\n(defun break-into-chunks (text chunk-size)\n29\n\"Breaks TEXT into chunks of size CHUNK-SIZE.\"\n30\n(loop for start from 0 below (length text) by chunk-size\n31\ncollect (subseq text start (min (+ start chunk-size) (length text)))))\n32\n33\n(defun decode-row (row)\n34\n(let ((id (nth 0 row))\n35\n(context (nth 1 row))\n36\n(embedding (read-from-string (nth 2 row))))\n37\n(list id context embedding)))\nThe next listing shows of parts of docs-qa.lisp that contain code to use SqLite. I wrapped the calls\nto initialize the database inside of handler-case for convenience during development (file reloads\ndon’t throw top level errors and the existing database is untouched).\n1\n(defvar *db* (connect \":memory:\"))\n2\n;;(defvar *db* (connect \"test.db\"))\n3\n4\n(pprint *db*)\n5\n(handler-case\n6\n(progn\n7\n(execute-non-query\n8\n*db*\n9\n\"CREATE TABLE documents (document_path TEXT, content TEXT, embedding TEXT);\")\n10\n(execute-non-query\n11\n*db*\n12\n\"CREATE INDEX idx_documents_id ON documents (document_path);\")\n\nUsing a Local Document Embeddings Vector Database With OpenAI GPT3 APIs for Semantically Querying Your Own Data\n280\n13\n(execute-non-query\n14\n*db*\n15\n\"CREATE INDEX idx_documents_content ON documents (content);\")\n16\n(execute-non-query\n17\n*db*\n18\n\"CREATE INDEX idx_documents_embedding ON documents (embedding);\"))\n19\n(error (c)\n20\n(print \"Database and indices is already created\")))\n21\n22\n(defun insert-document (document_path content embedding)\n23\n;;(format t \"insert-document:~% content:~A~%\nembedding: ~A~%\" content embedding)\n24\n(format t \"~%insert-document:~%\ncontent:~A~%~%\" content)\n25\n(execute-non-query\n26\n*db*\n27\n\"INSERT INTO documents (document_path, content, embedding) VALUES (?, ?, ?);\"\n28\ndocument_path content (write-floats-to-string embedding)))\n29\n30\n(defun get-document-by-document_path (document_path)\n31\n(mapcar #'decode-row\n32\n(execute-to-list *db*\n33\n\"SELECT * FROM documents WHERE document_path = ?;\"\n34\ndocument_path)))\n35\n36\n(defun get-document-by-content (content)\n37\n(mapcar #'decode-row\n38\n(execute-to-list *db*\n39\n\"SELECT * FROM documents WHERE content LIKE ?;\" content)))\n40\n41\n(defun get-document-by-embedding (embedding)\n42\n(mapcar #'decode-row\n43\n(execute-to-list *db*\n44\n\"SELECT * FROM documents WHERE embedding LIKE ?;\" embedding)))\n45\n46\n(defun all-documents ()\n47\n(mapcar #'decode-row\n48\n(execute-to-list *db* \"SELECT * FROM documents;\")))\n49\n50\n(defun create-document (fpath)\n51\n(let ((contents (break-into-chunks (read-file fpath) 200)))\n52\n(dolist (content contents)\n53\n(handler-case\n54\n(let ((embedding (openai::embeddings content)))\n55\n(insert-document fpath content embedding))\n\nUsing a Local Document Embeddings Vector Database With OpenAI GPT3 APIs for Semantically Querying Your Own Data\n281\n56\n(error (c)\n57\n(format t \"Error: ~&~a~%\" c))))))\nUsing Local Embeddings Vector Database With OpenAI\nGPT APIs\nThe next listing showing of parts of docs-qa.lisp interfaces with the OpenAI APIs:\n1\n(defun qa (question)\n2\n(let ((answer (openai:answer-question question 60)))\n3\n(format t \"~&~a~%\" answer)))\n4\n5\n(defun semantic-match (query custom-context &optional (cutoff 0.7))\n6\n(let ((emb (openai::embeddings query))\n7\n(ret))\n8\n(dolist (doc (all-documents))\n9\n(let ((context (nth 1 doc)) ;; ignore fpath for now\n10\n(embedding (nth 2 doc)))\n11\n(let ((score (openai::dot-product emb embedding)))\n12\n(when (> score cutoff)\n13\n(push context ret)))))\n14\n(format t \"~%semantic-search: ret=~A~%\" ret)\n15\n(let* ((context (join-strings \" . \" (reverse ret)))\n16\n(query-with-context\n17\n(join-strings\n18\n\" \"\n19\n(list context custom-context\n20\n\"Question:\" query))))\n21\n(openai:answer-question query-with-context 40))))\n22\n23\n(defun QA (query &optional (quiet nil))\n24\n(let ((answer (semantic-match query \"\")))\n25\n(if (not quiet)\n26\n(format t \"~%~%** query: ~A~%** answer: ~A~%~%\" query answer))\n27\nanswer))\n\nUsing a Local Document Embeddings Vector Database With OpenAI GPT3 APIs for Semantically Querying Your Own Data\n282\nTesting Local Embeddings Vector Database With\nOpenAI GPT APIs\nIn the next part of the listing of docs-qa.lisp we write a test function to create two documents. The\ntwo calls to create-document actually save text and embeddings for about 20 text chunks in the\ndatabase.\n1\n(defun test()\n2\n\"Test code for Semantic Document Search Using\n3\nOpenAI GPT APIs and local vector database\"\n4\n(create-document \"data/sports.txt\")\n5\n(create-document \"data/chemistry.txt\")\n6\n(QA \"What is the history of the science of chemistry?\")\n7\n(QA \"What are the advantages of engainging in sports?\"))\nThe output is (with a lot of debug printout not shown):\n1\n$ sbcl\n2\n* (quicklisp:quickload :docs-qa)\n3\nTo load \"docs-qa\":\n4\nLoad 1 ASDF system:\n5\ndocs-qa\n6\n; Loading \"docs-qa\"\n7\n..................................................\n8\n[package docs-qa]To load \"sqlite\":\n9\nLoad 1 ASDF system:\n10\nsqlite\n11\n; Loading \"sqlite\"\n12\n13\n#<sqlite-handle {7005CA3783}>\n14\n(:docs-qa)\n15\n* (in-package :docs-qa)\n16\n#<package \"DOCS-QA\">\n17\n* (test)\n18\n19\n** query: What is the history of the science of chemistry?\n20\n** answer: The history of chemistry as a science began in the 6th century BC, when t\\\n21\nhe Greek philosopher Leucippus and his student Democritus posited the existence of a\\\n22\nn endless number of worlds\n23\n24\n** query: What are the advantages of engainging in sports?\n\nUsing a Local Document Embeddings Vector Database With OpenAI GPT3 APIs for Semantically Querying Your Own Data\n283\n25\n** answer: The advantages of engaging in sports are:n1. It helps to develop the body\\\n26\nand mind.n2. It helps to develop the character.n3. It helps to develop the personal\\\n27\nity.\nAdding Chat History\nIn the last part of the listing of docs-qa.lisp we experiment with supporting a conversation/chat of\nmultiple semantic queries against our local documents.\n1\n(defun CHAT ()\n2\n(let ((messages '(\"\"))\n3\n(responses '(\"\")))\n4\n(loop\n5\n(format t \"~%Enter chat (STOP or empty line to stop) >> \")\n6\n(let ((string (read-line))\n7\nresponse)\n8\n(cond ((or (string= string \"STOP\") (< (length string) 1)) (return))\n9\n(t (let (prompt\n10\ncustom-context)\n11\n(setf custom-context\n12\n(concatenate\n13\n'string\n14\n\"PREVIOUS CHAT: \"\n15\n(join-strings\n\" \"\n16\n(reverse messages))))\n17\n(push string messages)\n18\n(print messages) ;; (print responses)\n19\n(print prompt)\n20\n(setf response (semantic-match string custom-context))\n21\n(push response responses)\n22\n(format t \"~%Response: ~A~%\" response))))))\n23\n(list (reverse messages) (reverse responses))))\nThe output (with lots of debug printouts removed) looks like:\n\nUsing a Local Document Embeddings Vector Database With OpenAI GPT3 APIs for Semantically Querying Your Own Data\n284\n1\n$ sbcl\n2\n* (quicklisp:quickload :docs-qa)\n3\nTo load \"docs-qa\":\n4\nLoad 1 ASDF system:\n5\ndocs-qa\n6\n; Loading \"docs-qa\"\n7\n..................................................\n8\n[package docs-qa].To load \"sqlite\":\n9\nLoad 1 ASDF system:\n10\nsqlite\n11\n; Loading \"sqlite\"\n12\n#<sqlite-handle {7005D9B9D3}>\n13\n* (in-package :docs-qa)\n14\n#<package \"DOCS-QA\">\n15\n* (create-document \"data/chemistry.txt\")\n16\n17\ninsert-document:\n18\ncontent:Amyl alcohol is an organic compound with the formula C 5 H 12 O. All eight\\\n19\nisomers of amyl alcohol are known. The most important is isobutyl carbinol, this be\\\n20\ning the chief constituent of fermentation\n21\n;; output from all other document chunks is not shown\n22\n23\n* (CHAT)\n24\n25\nEnter chat (STOP or empty line to stop) >> what is the history of chemistry?\n26\n27\nResponse: Chemistry is the science of matter, its composition, structure and its pro\\\n28\nperties. Chemistry is concerned with atoms and their interactions with other atoms, \\\n29\nand thus is central to all other sciences. Chemistry is also concerned\n30\n31\nEnter chat (STOP or empty line to stop) >> what is the boiling temperature?\n32\n33\nResponse: The boiling temperature of a liquid is the temperature at which the vapor \\\n34\npressure of the liquid equals the pressure surrounding the liquid, and the liquid ch\\\n35\nanges into a vapor. At the boiling temperature, bubbles of vapor\n36\n37\nEnter chat (STOP or empty line to stop) >>\n\nUsing a Local Document Embeddings Vector Database With OpenAI GPT3 APIs for Semantically Querying Your Own Data\n285\nWrap Up for Using Local Embeddings Vector Database\nto Enhance the Use of GPT3 APIs With Local\nDocuments\nAs I write this in early April 2023, I have been working almost exclusively with OpenAI APIs for the\nlast year and using the Python libraries for LangChain and LlamaIndex for the last three months.\nI prefer using Common Lisp over Python when I can, so I am implementing a tiny subset of\nthe LangChain and LlamaIndex libraries in Common Lisp for my own use. By writing about\nmy Common Lisp experiments here I hope that I get pull requests for https://github.com/mark-\nwatson/docs-qa¹⁰⁷ from readers who are interested in helping to extend the Common Lisp library.\n¹⁰⁷https://github.com/mark-watson/docs-qa\n\nPrompt Engineering for Large\nLanguage Models\nWe have been using prompt engineering in an informal way in the last few chapters. In this chapter\nwe look at prompt engineering in more detail. My hope is that, dear reader, you find this chapter to\nbe both a short reference for Large Language Models (LLMS) and a useful source of prompt examples\nat the end of the chapter.\nTwo Types of LLMS\nIn order to avoid “model hallucinations” (i.e., models making up answers with no basis in fact) when\nusing Large Language Models (LLMS) it is important to explain in detail instructions and add context\ntext to help the model better answer questions of solve problems.\nBase LLMs and Instruction Tuned LLMs are both types of advanced AI language models. While\nthey share some similarities in their underlying architecture and training processes, they have\ndistinct differences in terms of their fine-tuning and usage. Here, we will discuss the similarities\nand differences in detail.\nThe similarities are:\n• Architecture: Both models are based on the Transformer architecture, which has been shown\nto be highly effective for various natural language processing tasks. This architecture relies on\nself-attention mechanisms to process input data and generate outputs.\n• Pretraining: Both models undergo a pretraining phase where they learn from a large corpus\nof text, such as websites, books, and articles. This helps the models acquire a general\nunderstanding of language, grammar, facts, and some reasoning abilities.\n• Transfer learning: Both models can use transfer learning, which involves fine-tuning the\npretrained models on specific tasks or datasets. This process adapts the model’s general\nknowledge to the target application, improving its performance on the desired task.\nThe differences are:\n• Fine-tuning objectives: The key difference between the two models lies in their fine-tuning\nprocess. Base Large Language Models are fine-tuned to generate human-like text with minimal\nexplicit guidance, whereas Instruction Tuned Large Language Models are specifically optimized\nto follow instructions provided in the input prompt.\n\nPrompt Engineering for Large Language Models\n287\n• Task performance: Due to their different fine-tuning objectives, Instruction Tuned Large\nLanguage Models tend to perform better on tasks that require following explicit instructions,\nwhile Base Large Language Models may produce more diverse and creative responses that may\nnot always adhere to the given instructions.\n• Usage: Base Large Language Models can be used for a wide variety of tasks, including\ntext generation, summarization, translation, and question-answering. Instruction Tuned Large\nLanguage Models, on the other hand, are designed to be more controllable and responsive\nto input instructions, making them more suitable for applications that require precise output\nbased on specific instructions.\n• Prompt engineering: To get the desired output from a Base Large Language Model, users often\nneed to carefully craft prompts, which can be a trial-and-error process. With Instruction Tuned\nLarge Language Models, users can provide more explicit instructions to achieve the desired\noutput more consistently.\nIn summary, both Base and Instruction Tuned Large Language Models share the same foundation\nin terms of architecture and pretraining but differ in their fine-tuning objectives and application\nstrengths. While Base Large Language Models generate more diverse and creative text, Instruction\nTuned Large Language Models excel at tasks that require following specific instructions and are\nmore controllable.\nPrompt Examples\nWe have used both OpenAI and Hugging Face Models so far in this book. When you practice prompt\nengineering and learn new techniques, your knowledge of prompt engineering can be applied to the\nOpenAI and Hugging Face APIs and models.\nAs Andrew Ng and Isa Fulford point out in their one hour class ChatGPT Prompt Engineering\nfor Developers¹⁰⁸, you can craft prompts as if you were instructing an intelligent person who is\nnot necessarily familiar with the current problem you are trying to solve with prompt engineering.\nThere is a bit of a tradeoff here: you pay per input and output tokens with the OpenAI APIs so you\nmay be tempted to compress your context text, instructions, and questions when creating prompts.\nHowever, in general to more context and information you can provide increases the quality of the\nresulting output.\nThe following sections show examples of prompt engineering that I use in my own work. In order\nto learn more techniques I recommend also performing web search for prompt engineering with a\ndescription of the problem you are working on. As an example, I asked Microsoft Bing+ChatGPT in\na chat to help me with I need help with prompt engineering using the OpenAI APIs. My problem\nis: I have a text file that contains customer names and phone numbers. I need a prompt to list\nnames and phone numbers. and the advice was a useful starting point. In a similar way I received\nuseful advice when chatting with Google Bard.\n¹⁰⁸https://learn.deeplearning.ai/chatgpt-prompt-eng/lesson\n\nPrompt Engineering for Large Language Models\n288\nFind Entities and Relationships\nPrompt:\n1\nThe task is to extract as many relevant entities and the relationships between these\\\n2\nentities.\\n\n3\nWhen possible, return the type of an entity using the Wikipedia class system.\\n\n4\nExtract all relevant relationships between identified entities.\\n\n5\nThe relationships should follow the Wikipedia schema type.\n6\nThe output of a relationship should be in a form of a triple Head, Relationship, Tai\\\n7\nl, for example\n8\nBill Gates, BORN_AT, Seatle.\\n\n9\nAs an example \"Bill Gates worked at Microsoft\" should have an output with the follow\\\n10\ning format:\\n\n11\nentity\n12\nBill Gates, person\n13\nMicrosoft, organization\n14\n15\nrelationships\n16\nBill Gates, WORKET AT, Microsoft\\n\n17\nTask: process this text:\n18\nSteve Jobes founded Apple Computer and Bill Gates started Microsoft. Seattle is in t\\\n19\nhe state of Washington.\n20\nSteve lived in Palo Alto. Palo Alto is located in California.\nChatGPT 4 returns:\n1\nentities\n2\nSteve Jobs, person\n3\nApple Computer, organization\n4\nBill Gates, person\n5\nMicrosoft, organization\n6\nSeattle, city\n7\nWashington, administrative territorial entity\n8\n9\nrelationships\n10\nSteve Jobs, FOUNDED, Apple Computer\n11\nBill Gates, FOUNDED, Microsoft\n12\nSeattle, IS_IN, Washington\nDisambiguate Relationship Names\nPrompt:\n\nPrompt Engineering for Large Language Models\n289\n1\nGiven a list of relationships like:\n2\n3\nbrother\n4\nlocated in\n5\nsister\n6\nsibling\n7\nfound at\n8\nfather\n9\nlocation\n10\nmother\n11\nrelative\n12\n13\nThen return a list with an index for each unique relationship, like:\n14\n15\nbrother, 1\n16\nlocated in, 2\n17\nsister, 1\n18\nsibling, 1\n19\nfound at, 2\n20\nfather, 1\n21\nlocation, 2\n22\nmother, 1\n23\nrelative, 1\n24\n25\nTask: process the following text:\n26\n27\nSam has a brother Mike. Suzy has a sister Sally. Suszy ahs a mother Jill, and a fath\\\n28\ner Bill.\n29\nMike is in San Diego. Sam is located is Oakland. Suzy's family all live in Los Angel\\\n30\nes.\nChatGPT 4 results are:\n1\nBased on the provided list of relationships, here is the processed text with an inde\\\n2\nx for each unique relationship:\n3\n4\nSam has a brother Mike. (brother, 1)\n5\nSuzy has a sister Sally. (sister, 1)\n6\nSuzy has a mother Jill, and a father Bill. (mother, 1; father, 1)\n7\nMike is in San Diego. (located in, 2)\n8\nSam is located in Oakland. (located in, 2)\n9\nSuzy's family all live in Los Angeles. (location, 2)\n10\n\nPrompt Engineering for Large Language Models\n290\n11\nHere, relationships like \"brother,\" \"sister,\" \"mother,\" and \"father\" are assigned an\\\n12\nindex of 1, while relationships like \"located in\" and \"location\" are assigned an in\\\n13\ndex of 2.\nDisambiguate Relationship Names and Rewrite Text\nPrompt:\n1\nWe want to disambiguate relationship names and then rewrite text using canonical rel\\\n2\nationship names.\n3\n4\nGiven a list of relationships like:\n5\n6\nbrother\n7\nlocated in\n8\nsister\n9\nsibling\n10\nfound at\n11\nfather\n12\nlocation\n13\nmother\n14\nrelative\n15\n16\nThen generate a list with an index for each unique relationship, like:\n17\n18\nbrother, 1\n19\nlocated in, 2\n20\nsister, 1\n21\nsibling, 1\n22\nfound at, 2\n23\nfather, 1\n24\nlocation, 2\n25\nmother, 1\n26\nrelative, 1\n27\n28\nAnd then rewrite the original text with the relation indices.\n29\n30\nTask: process the following text:\n31\n32\nSam has a brother Mike. Suzy has a sister Sally. Suszy ahs a mother Jill, and a fath\\\n33\ner Bill.\n34\nMike is in San Diego. Sam is located is Oakland. Suzy's family all live in Los Angel\\\n35\nes.\n\nPrompt Engineering for Large Language Models\n291\nThe ChatGPT 4 output is:\n1\nFirst, we create the indexed list of unique relationships:\n2\n3\nbrother, 1\n4\nlocated in, 2\n5\nsister, 1\n6\nsibling, 1\n7\nfound at, 2\n8\nfather, 1\n9\nlocation, 2\n10\nmother, 1\n11\nrelative, 1\n12\n13\nNow, we rewrite the original text using the relation indices:\n14\n15\nSam has a 1 Mike. Suzy has a 1 Sally. Suszy has a 1 Jill, and a 1 Bill.\n16\nMike is 2 San Diego. Sam is 2 Oakland. Suzy's family all 2 Los Angeles.\nIn the first sentence Sam has a 1 Mike the number 1 refers to brother.\nDisambiguate Relationship Names and Rewrite Text, Only Output\nRewritten Text\nHere we output just results in JSON format.\nPrompt:\n1\nWe want to disambiguate relationship names and then rewrite text using canonical rel\\\n2\nationship names.\n3\n4\nGiven a list of relationships like:\n5\n6\nbrother\n7\nlocated in\n8\nsister\n9\nsibling\n10\nfound at\n11\nfather\n12\nlocation\n13\nmother\n14\nrelative\n15\n\nPrompt Engineering for Large Language Models\n292\n16\nThen generate a list with an index for each unique relationship, like:\n17\n18\nbrother, <1>\n19\nlocated in, <2>\n20\nsister, <1>\n21\nsibling, <1>\n22\nfound at, <2>\n23\nfather, <1>\n24\nlocation, <2>\n25\nmother, <1>\n26\nrelative, <1>\n27\n28\nAnd then rewrite the original text using the relation indices, and list relation nam\\\n29\nes with indices. Only output rewritten text and names with indices.\n30\n31\nTask: process the following text outputing JSON format:\n32\n33\nSam has a brother Mike. Suzy has a sister Sally. Suzy has a mother Jill, and a fathe\\\n34\nr Bill.\n35\nMike is in San Diego. Sam is located is Oakland. Suzy's family all live in Los Angel\\\n36\nes.\nThe ChatGPT 4 output is:\n1\n{\n2\n\"relationships\": {\n3\n\"brother\": \"<1>\",\n4\n\"located in\": \"<2>\",\n5\n\"sister\": \"<1>\",\n6\n\"sibling\": \"<1>\",\n7\n\"found at\": \"<2>\",\n8\n\"father\": \"<1>\",\n9\n\"location\": \"<2>\",\n10\n\"mother\": \"<1>\",\n11\n\"relative\": \"<1>\"\n12\n},\n13\n\"rewritten_text\": \"Sam has a brother Mike. Suzy has a sister Sally. Suzy has a mot\\\n14\nher Jill, and a father Bill.\\nMike is <2> San Diego. Sam is <2> Oakland. Suzy's fami\\\n15\nly all live <2> Los Angeles.\"\n16\n}\n\nPrompt Engineering for Large Language Models\n293\nTopic Modeling for Document Classification\nIn this example we show a complete template that includes document text. To use this in your\nprograms, replace the document text with a token string that you can replace with the text for the\ndocument you are topic modeling.\nPrompt:\n1\nTopic modeling involves reading text and assigning a topic name (also known as a doc\\\n2\nument classification).\n3\nThe topics that you know how to classify are:\n4\n5\nsports\n6\nhealth\n7\nchemistry\n8\neconomy\n9\npolitics\n10\n11\nThe output format will be:\n12\n13\nKnown topics: <list of all topics>\n14\nTopic: <topic of text>\n15\n16\nTask: what is the best topic name for the following text:\n17\n18\nJohn and Sam went to the playing field yesterday. Sam sprained his ankle.\nThe result is:\n1\nKnown topics: sports, health, chemistry, economy, politics\n2\n3\nTopic: sports\nIn this prompt template, I used placeholder document text John and Sam went to the playing field\nyesterday. Sam sprained his ankle. that you would replace with the longer text from the document\nyou want to determine the topic or classification of.\nIn this template, I like to ask the LLM to repeat the list of topics (or classifications) it knows.\nPrompt Engineering Wrapup\nWhen you experiment with the examples in this chapter you can both use the ChatGPT web interface\nto modify and develop prompts and you can also revisit the chapter on OpenAI APIs and experiment\n\nPrompt Engineering for Large Language Models\n294\nwith prompts programmatically. My personal preference is to perform initial experiments with the\nChatGPT web interface but to do most of the iterative prompt development in Emacs with a SLIME\nREPL.\nI mentioned earlier Andrew Ng’s and Isa Fulford’s one hour prompt engineering class. The devote\ntime and provide good advice on interactive developement. Their examples use the Python language.\n\nUsing Common Lisp with\nWolfram/One\nIf you use Wolfram/One¹⁰⁹ then the material in this short chapter may interest you. The interface\nthat I wrote is simple: I use uiop:run-program to spawn a new process to run the Wolfram Language\ncommand line tool that writes results to a temporary file. I then use uiop:read-file-string to read\nthe results and parse them into a convenient form for use.\nBefore we build and use an interface to Wolfram/One, let’s look at two screen shots of the\nWolfram/One interface with examples that we will run later in Common Lisp. The first example\nfinds entities in text:\nUsing Wolfram/One to find entities in text\nThe second example uses a deep learning model to answer a question given text containing the\nanswer to the question:\nUsing Wolfram/One to answer natural language questions\nHere is the package.lisp file for this example:\n¹⁰⁹https://www.wolfram.com/wolfram-one/\n\nUsing Common Lisp with Wolfram/One\n296\n1\n(defpackage #:wolfram\n2\n(:use #:cl #:uiop)\n3\n(:export #:wolfram #:cleanup-lists\n4\n#:find-answer-in-text #:entities))\nAnd the wolfram.asd file:\n1\n(asdf:defsystem #:wolfram\n2\n:description \"Wolfram Language interface experiments\"\n3\n:author \"Mark Watson <markw@markwatson.com>\"\n4\n:license \"Apache 2\"\n5\n:depends-on (#:uiop #:cl-json #:myutils)\n6\n:components ((:file \"package\")\n7\n(:file \"wolfram\")))\nThe implementation in Wolfram.lisp is simple enough. In lines 6-8 I create a Common Lisp path\nobject in /tmp (and absolute pathname is required) and then use file-namestring to get just the file\nname as a string. In lines 8-10 we are creating an operating system shell and running the Wolfram\nLanguage command line tool with arguments to execute the query and write the results to the\ntemporary file. In lines 11-15 we read the contents of the temporary file, delete the file, and decode\nthe returned string as JSON data.\nThe Data returned form calling the Wolfram Language command line tool contains excess structure\nthat we don’t need (a sample of the raw returned data is shown later) so the function cleanup-lists\nshown in lines 17-19 discards heads of lists when the first value in a list or sublist is Rule or List. The\nfunction recursive-remove seen in lines 20-24 will remove all occurrences of an item from a nested\nlist.\n1\n(in-package #:wolfram)\n2\n3\n;; General query utilities\n4\n5\n(defun wolfram (statement)\n6\n(let ((temp-file-path\n7\n(file-namestring (uiop:tmpize-pathname \"/tmp/wolfram\"))))\n8\n(uiop:run-program (concatenate 'string \"wolframscript -code 'Export[\\\"\"\n9\ntemp-file-path \"\\\",\" statement\n10\n\",\\\"ExpressionJSON\\\"]'\"))\n11\n(let* ((ret (uiop:read-file-string temp-file-path)))\n12\n(delete-file temp-file-path)\n13\n(with-input-from-string (s (myutils:replace-all\n14\n(myutils:replace-all ret \"\\\"'\" \"\\\"\") \"'\\\"\" \"\\\"\"))\n15\n(json:decode-json s)))))\n\nUsing Common Lisp with Wolfram/One\n297\n16\n17\n(defun cleanup-lists (r)\n18\n(cdr (recursive-remove \"Rule\" (recursive-remove \"List\" r))))\n19\n20\n(defun recursive-remove (item tree)\n21\n(if (atom tree)\n22\ntree\n23\n(mapcar (lambda (nested-list) (recursive-remove item nested-list))\n24\n(remove item tree :test #'equal))))\n25\n26\n;; Higher level utilities for specific types of queries\n27\n28\n(defun entities (text)\n29\n(let* ((noquotes (myutils:replace-all (myutils:replace-all text \"\\\"\" \" \") \"'\" \" \"))\n30\n(query2\n31\n(concatenate\n32\n'string \"TextCases['\" noquotes\n33\n\"',\n{'City', 'Country', 'Date', 'Person'} ->\"\n34\n\" {'String', 'Interpretation', 'Probability'}]\"))\n35\n(query (myutils:replace-all query2 \"'\" \"\\\"\")))\n36\n(remove-if #'(lambda (a) (null (cadr a)))\n37\n(cleanup-lists (wolfram query)))))\n38\n39\n(defun find-answer-in-text (text question)\n40\n(let* ((nqtext (myutils:replace-all (myutils:replace-all text \"\\\"\" \" \") \"'\" \" \"))\n41\n(nqquestion (myutils:replace-all\n42\n(myutils:replace-all question \"\\\"\" \" \") \"'\" \" \"))\n43\n(query2 (concatenate 'string \"FindTextualAnswer['\" nqtext\n44\n\"', '\" nqquestion \"']\"))\n45\n(query (myutils:replace-all query2 \"'\" \"\\\"\")))\n46\n(wolfram query)))\nThe last two functions in the last code listing, entities and find-answer-in-text are higher level\nfunctions intended to work with the Wolfram Language procedures TextCases (see Wolfram\ndocumentation for TextCases¹¹⁰) and FindTextualAnswer (see Wolfram documentation for Find-\nTextualAnswer¹¹¹).\nThe functions cleanup-lists and recursive-remove can be used to clean up results. First, we will\njust call function wolfram and show the raw results:\n¹¹⁰https://reference.wolfram.com/language/ref/TextCases.html\n¹¹¹https://reference.wolfram.com/language/ref/FindTextualAnswer.html\n\nUsing Common Lisp with Wolfram/One\n298\n1\n$ sbcl\n2\n* (ql:quickload \"wolfram\")\n3\nTo load \"wolfram\":\n4\nLoad 1 ASDF system:\n5\nwolfram\n6\n; Loading \"wolfram\"\n7\n[package myutils].................................\n8\n[package wolfram]\n9\n(\"wolfram\")\n10\n* (setf example \"TextCases['NYC, Los Angeles, and Chicago are the largest cities in \\\n11\nthe USA in 2018 according to Pete Wilson.', {'City', 'Country', 'Date', 'Person'} ->\\\n12\n{'String', 'Interpretation', 'Probability'}]\")\n13\n\"TextCases['NYC, Los Angeles, and Chicago are the largest cities in the USA in 2018 \\\n14\naccording to Pete Wilson.', {'City', 'Country', 'Date', 'Person'} -> {'String', 'Int\\\n15\nerpretation', 'Probability'}]\"\n16\n* (setf example-str (myutils:replace-all\nexample \"'\" \"\\\"\"))\n17\n\"TextCases[\\\"NYC, Los Angeles, and Chicago are the largest cities in the USA in 2018\\\n18\naccording to Pete Wilson.\\\", {\\\"City\\\", \\\"Country\\\", \\\"Date\\\", \\\"Person\\\"} -> {\\\"St\\\n19\nring\\\", \\\"Interpretation\\\", \\\"Probability\\\"}]\"\n20\n* (setf results (wolfram:wolfram example-str))\n21\n* (pprint results)\n22\n23\n(\"Association\"\n24\n(\"Rule\" \"City\"\n25\n(\"List\"\n26\n(\"List\" \"NYC\" (\"Entity\" \"City\" (\"List\" \"NewYork\" \"NewYork\" \"UnitedStates\"))\n27\n0.75583166)\n28\n(\"List\" \"Los Angeles\"\n29\n(\"Entity\" \"City\" (\"List\" \"LosAngeles\" \"California\" \"UnitedStates\"))\n30\n0.84206486)\n31\n(\"List\" \"Chicago\"\n32\n(\"Entity\" \"City\" (\"List\" \"Chicago\" \"Illinois\" \"UnitedStates\"))\n33\n0.91092855)))\n34\n(\"Rule\" \"Country\"\n35\n(\"List\" (\"List\" \"USA\" (\"Entity\" \"Country\" \"UnitedStates\") 0.9285077)))\n36\n(\"Rule\" \"Date\"\n37\n(\"List\"\n38\n(\"List\" \"2018\" (\"DateObject\" (\"List\" 2018) \"Year\" \"Gregorian\" -7.0)\n39\n0.8364356)))\n40\n(\"Rule\" \"Person\"\n41\n(\"List\"\n42\n(\"List\" \"Pete Wilson\" (\"Entity\" \"Person\" \"PeteWilson::s7259\") 0.9274548))))\n43\n*\n\nUsing Common Lisp with Wolfram/One\n299\nNow we clean up the output:\n1\n* (defvar results-cleaned (wolfram:cleanup-lists results))\n2\n* (pprint results-cleaned)\n3\n4\n((\"City\"\n5\n((\"NYC\" (\"Entity\" \"City\" (\"NewYork\" \"NewYork\" \"UnitedStates\")) 0.75583166)\n6\n(\"Los Angeles\" (\"Entity\" \"City\" (\"LosAngeles\" \"California\" \"UnitedStates\"))\n7\n0.84206486)\n8\n(\"Chicago\" (\"Entity\" \"City\" (\"Chicago\" \"Illinois\" \"UnitedStates\"))\n9\n0.91092855)))\n10\n(\"Country\" ((\"USA\" (\"Entity\" \"Country\" \"UnitedStates\") 0.9285077)))\n11\n(\"Date\" ((\"2018\" (\"DateObject\" (2018) \"Year\" \"Gregorian\" -7.0) 0.8364356)))\n12\n(\"Person\" ((\"Pete Wilson\" (\"Entity\" \"Person\" \"PeteWilson::s7259\") 0.9274548))))\n13\n*\nNext we will try the two higher-level utility functions. The first example shows finding entities in\ntext:\n1\nCL-USER 21 > (pprint\n2\n(wolfram:entities \"Sedona Arizona is home to Mark Louis Watson\"))\n3\n4\n((\"City\"\n5\n((\"Sedona\" (\"Entity\" \"City\" (\"Sedona\" \"Arizona\" \"UnitedStates\")) 0.8392784)))\n6\n(\"Person\" ((\"Mark Louis Watson\" \"Mark Louis Watson\" 0.9023427))))\nThe second example uses a Wolfram pre-trained deep learning model for question answering:\n1\nCL-USER 22 > (pprint\n2\n(wolfram::find-answer-in-text \"International Business Machines Corpor\\\n3\nation (IBM) is an American multinational technology company headquartered in Armonk,\\\n4\nNew York, with operations in over 170 countries. The company began in 1911, founded\\\n5\nin Endicott, New York, as the Computing-Tabulating-Recording Company (CTR) and was \\\n6\nrenamed \\\"International Business Machines\\\" in 1924. IBM is incorporated in New York\\\n7\n.\"\n8\n\"where is IBM is headquartered?\"))\n9\n10\n\"Armonk, New York\"\nIf you use Wolfram/One then these examples should get you started wrapping other Wolfram\nLanguage functionality for use in your Common Lisp applications.\n\nBook Wrapup\nCongratulations for finishing this book!\nI love programming in Lisp languages with concise code and a bottom-up approach to development.\nI hope you now also share this enthusiasm with me.\nCommon Lisp is sometimes criticised as not having as many useful libraries as some newer languages\nlike Python and Java, and this is a valid criticism. That said, I hope the wide variety of examples\nin this book will convince you that Common Lisp is a good choice for many types of programming\nprojects.\nI would like to thank you for reading my book and I hope that you enjoyed it. As I mentioned in the\nIntroduction I have been using Common Lisp since the mid-1980s, and other Lisp dialects for longer\nthan that. I have always found something almost magical developing in Lisp. Being able to extend\nthe language with macros and using the development technique of building a mini-language in Lisp\ncustomized for an application enables programmers to be very efficient in their work. I have usually\nfound that this bottom-up development style helps me deal with software complexity because the\nlower level functions tend to get well tested while the overall system being developed is not yet\ntoo complex to fully understand. Later in the development process these lower level functions and\nutilities almost become part of the programming language and the higher level application logic is\neasier to understand because you have fewer lines of code to fit inside your head during development.\nI think that unless a programmer works in very constrained application domains, it often makes sense\nto be a polyglot programmer. I have tried, especially in the new material for this fourth edition, to\ngive you confidence that Common Lisp is good for both general software development language and\nalso as “glue” to tie different systems together.\nThank you for buying and reading my book!\nMark Watson\n\n"
  },
  {
    "path": "embedchain_test/process_pdfs.py",
    "content": "\n# https://github.com/embedchain/embedchain\n\nfrom embedchain import App\nimport os\n\ntest_chat = App()\n\nmy_books_dir = \"./data/\"\n\nfor filename in os.listdir(my_books_dir):\n    if filename.endswith('.pdf'):\n        print(\"processing filename:\", filename)\n        test_chat.add(os.path.join(my_books_dir, filename))\n\n"
  },
  {
    "path": "embedchain_test/requirements.txt",
    "content": "embedchain=0.1.123\n\n"
  },
  {
    "path": "extraction/README.md",
    "content": "# Extraction from text\n\n"
  },
  {
    "path": "extraction/person_data.py",
    "content": "import openai\nfrom openai import OpenAI\nimport os\n\nopenai.api_key = os.getenv(\"OPENAI_API_KEY\")\nclient = OpenAI()\n\n# Read the prompt from a text file\nwith open('prompt.txt', 'r') as file:\n    prompt_template = file.read()\n\n# Substitute a string variable into the prompt\ninput_text = \"Mark Johnson enjoys living in Berkeley California at 102 Dunston Street and use mjess@foobar.com for contacting him.\"\nprompt = prompt_template.replace(\"input_text\", input_text)\n\n# Use the OpenAI completion API to generate a response with GPT-4\ncompletion = client.chat.completions.create(\n    model=\"gpt-4\",\n    messages=[\n        {\n            \"role\": \"user\",\n            \"content\": prompt,\n        },\n    ],\n)\n\nprint(completion.choices[0].message.content)\n"
  },
  {
    "path": "extraction/prompt.txt",
    "content": "Given the two examples below, extract the names, addresses, and email addresses of individuals mentioned later as Process Text. Format the extracted information in JSON, with keys for \"name\", \"address\", and \"email\". If any information is missing, use \"null\" for that field.\n\nExample 1:\nText: \"John Doe lives at 1234 Maple Street, Springfield. His email is johndoe@example.com.\"\nOutput: \n{\n  \"name\": \"John Doe\",\n  \"address\": \"1234 Maple Street, Springfield\",\n  \"email\": \"johndoe@example.com\"\n}\n\nExample 2:\nText: \"Jane Smith has recently moved to 5678 Oak Avenue, Anytown. She hasn't updated her email yet.\"\n Output: \n{\n  \"name\": \"Jane Smith\",\n  \"address\": \"5678 Oak Avenue, Anytown\",\n  \"email\": null\n}\n\nProcess Text: \"{input_text}\"\nOutput:\n"
  },
  {
    "path": "from_langchain_docs/README.md",
    "content": "pip install -U langchain-openai\npip install -U langchain-community\npip install langchainhub\n\nNote: I no longer use Serp so I have not checked the search example recently.\n\n# new example using OpenAI model gpt-4o released May 13, 2024\n\n    python gpt_4o_test.py\n"
  },
  {
    "path": "from_langchain_docs/gpt_4o_test.py",
    "content": "from langchain_core.messages import HumanMessage, SystemMessage\nfrom langchain_openai import ChatOpenAI\n\nllm = ChatOpenAI(model=\"gpt-4o\") # gpt-4o-mini is less expensive and almost as good\n\nmessages = [\n    SystemMessage(content=\"You're a helpful assistant\"),\n    HumanMessage(content=\"What is the purpose of model regularization? Be concise.\"),\n]\n\nresults = llm.invoke(messages)\nprint(results.content)\nprint(\"\\n\\n\\n\")\nprint(results)"
  },
  {
    "path": "from_langchain_docs/memory_langchain_test.py",
    "content": "# Derived from:\n#   https://langchain.readthedocs.io/en/latest/modules/memory/examples/adding_memory.html\n# with slight modifications.\n\nfrom langchain.chains.conversation.memory import ConversationBufferMemory\nfrom langchain import OpenAI, LLMChain, PromptTemplate\n\ntemplate = \"\"\"You are a chatbot having a conversation with a human.\n\n{chat_history}\nHuman: {human_input}\nChatbot:\"\"\"\n\nprompt = PromptTemplate(\n    input_variables=[\"chat_history\", \"human_input\"], \n    template=template\n)\nmemory = ConversationBufferMemory(memory_key=\"chat_history\")\n\nllm_chain = LLMChain(\n    llm=OpenAI(), \n    prompt=prompt, \n    verbose=True, \n    memory=memory,\n)\n\nprint(llm_chain.predict(human_input=\"Hi there my friend. What is your name?\"))\n\nprint(llm_chain.predict(human_input=\"My name is Mark. How are you?\"))\n\nprint(llm_chain.predict(human_input=\"What do you have planned for today?\"))\n"
  },
  {
    "path": "from_langchain_docs/requirements.txt",
    "content": "langchain_core\nlangchain_openai\nlangchain_community\n"
  },
  {
    "path": "from_langchain_docs/search_simple.py",
    "content": "# make sure SERPER_API_KEY is set in your environment\n\nfrom langchain_community.utilities import GoogleSerperAPIWrapper\nsearch_helper = GoogleSerperAPIWrapper()\n\ndef search(query):\n    return search_helper.run(query)\n\nprint(search(\"What is the capital of Arizona?\"))\n#print(search(\"Sedona Arizona?\"))"
  },
  {
    "path": "google_drive_llm/.gitignore",
    "content": "client_secrets.json\nclie*.json\n\n"
  },
  {
    "path": "google_drive_llm/README.md",
    "content": "# Setup\n\nFollow directions in the book to set up Google app authentication.\n\n# run\n\n```\npython fetch_txt_files.py\npython index_and_QA.py\n```\n\nNote: I added a directory langchain-book-examples/google_drive_llm/data that you can use if you don't want to set up Google auth, and ust want to try running index_and_QA.py\n"
  },
  {
    "path": "google_drive_llm/data/sports.txt",
    "content": "Sport is generally recognised as activities based in physical athleticism or physical dexterity.[3] Sports are usually governed by rules to ensure fair competition and consistent adjudication of the winner.\n\n\"Sport\" comes from the Old French desport meaning \"leisure\", with the oldest definition in English from around 1300 being \"anything humans find amusing or entertaining\".[4]\n\nOther bodies advocate widening the definition of sport to include all physical activity and exercise. For instance, the Council of Europe include all forms of physical exercise, including those completed just for fun.\n\n"
  },
  {
    "path": "google_drive_llm/fetch_txt_files.py",
    "content": "from pydrive.auth import GoogleAuth\nfrom pydrive.drive import GoogleDrive\nfrom pathlib import Path\n\n# good GD search docs: https://developers.google.com/drive/api/guides/search-files#all\n\n# Authenticate with Google\ngauth = GoogleAuth()\ngauth.LocalWebserverAuth()\ndrive = GoogleDrive(gauth)\n\ndrive = GoogleDrive(gauth)\n\ndef get_txt_files(dir_id='root'):\n    \" get all plain text files with .txt extension in top level Google Drive directory \"\n\n    file_list = drive.ListFile({'q': f\"'{dir_id}' in parents and trashed=false\"}).GetList()\n    for file1 in file_list:\n        print('title: %s, id: %s' % (file1['title'], file1['id']))\n    return [[file1['title'], file1['id'], file1.GetContentString()]\n            for file1 in file_list if file1['title'].endswith(\".txt\")]\n\ndef create_test_file():\n    \" not currently used, but useful for testing. \"\n\n    # Create GoogleDriveFile instance with title 'Hello.txt':\n    file1 = drive.CreateFile({'title': 'Hello.txt'})\n    file1.SetContentString('Hello World!')\n    file1.Upload()\n\ndef test():\n    fl = get_txt_files()\n    for f in fl:\n        print(f)\n        file1 = open(\"data/\" + f[0],\"w\")\n        file1.write(f[2])\n        file1.close()\n\nif __name__ == '__main__':\n    test()\n"
  },
  {
    "path": "google_drive_llm/index_and_QA.py",
    "content": "# make sure you set the following environment variable is set:\n#   OPENAI_API_KEY\n\nfrom llama_index import GPTSimpleVectorIndex, SimpleDirectoryReader\ndocuments = SimpleDirectoryReader('data').load_data()\nindex = GPTSimpleVectorIndex(documents)\n\n# save to disk\nindex.save_to_disk('index.json')\n# load from disk\nindex = GPTSimpleVectorIndex.load_from_disk('index.json')\n\n# search for a document\nprint(index.query(\"What is the definition of sport?\"))\n"
  },
  {
    "path": "google_drive_llm/requirements.txt",
    "content": "pydrive\n"
  },
  {
    "path": "hugging_face/README.md",
    "content": "# Using local models\n\npip install -U torch langchain transformers llama_index\n\nFor CPU only, this speeds things up:\n\npip install xformers\n"
  },
  {
    "path": "hugging_face/hf_transformer_local.py",
    "content": "# Derived from example:\n#   https://gpt-index.readthedocs.io/en/latest/how_to/custom_llms.html\n\nimport time\nimport torch\nfrom langchain.llms.base import LLM\nfrom llama_index import SimpleDirectoryReader, LangchainEmbedding\nfrom llama_index import ListIndex, PromptHelper\nfrom llama_index import LLMPredictor\nfrom transformers import pipeline\n\nmax_input_size = 512\nnum_output = 64\nmax_chunk_overlap = 0 # 10\nprompt_helper = PromptHelper(max_input_size, num_output, max_chunk_overlap)\n\nclass CustomLLM(LLM):\n    model_name = \"facebook/opt-iml-1.3b\"\n    # I am not using a GPU, but you can add device=\"cuda:0\"\n    # to the pipeline call if you have a local GPU or\n    # are running this on Google Colab:\n    pipeline = pipeline(\"text-generation\", model=model_name,\n                        model_kwargs={\"torch_dtype\":torch.bfloat16})\n\n    def _call(self, prompt, stop = None):\n        prompt_length = len(prompt)\n        response = self.pipeline(prompt, max_new_tokens=num_output)\n        first_response = response[0][\"generated_text\"]\n        # only return newly generated tokens\n        returned_text = first_response[prompt_length:]\n        return returned_text\n\n    @property\n    def _identifying_params(self):\n        return {\"name_of_model\": self.model_name}\n\n    @property\n    def _llm_type(self):\n        return \"custom\"\n\ntime1 = time.time()\n\n# define our LLM\nllm_predictor = LLMPredictor(llm=CustomLLM())\n\n# Load the your data\ndocuments = SimpleDirectoryReader('../data_small').load_data()\n# llama_index < 0.5:\n#index = GPTListIndex(documents, llm_predictor=llm_predictor,\n#                     prompt_helper=prompt_helper)\n\n# llama_index >= 0.5: (not yet working)\nindex = ListIndex.from_documents(documents=documents, \n                                 llm_predictor=llm_predictor,\n                                 prompt_helper=prompt_helper)\n#index = index.from_documents(documents)\nindex = index.as_query_engine(llm_predictor=llm_predictor)\n\ntime2 = time.time()\nprint(f\"Time to load model from disk: {time2 - time1} seconds.\")\n\nprint(dir(index))\n# Query and print response\nresponse = index.query(\"What is the definition of sport?\")\nprint(response)\n\ntime3 = time.time()\nprint(f\"Time for query/prediction: {time3 - time2} seconds.\")"
  },
  {
    "path": "hugging_face/requirements.txt",
    "content": "numpy==1.22.0\nllama-index==0.10.13\nsentence-transformers\n"
  },
  {
    "path": "hugging_face/simple_example.py",
    "content": "from langchain import HuggingFaceHub, LLMChain\nfrom langchain.prompts import PromptTemplate\n\nhub_llm = HuggingFaceHub(\n    repo_id='google/flan-t5-xl',\n    model_kwargs={'temperature':1e-6}\n)\n\nprompt = PromptTemplate(\n    input_variables=[\"name\"],\n    template=\"What year did {name} get elected as president?\",\n)\n\nllm_chain = LLMChain(prompt=prompt, llm=hub_llm)\n\nprint(llm_chain.run(\"George Bush\"))\n\n"
  },
  {
    "path": "hugging_face/test1.py",
    "content": "# pip install xformers\n\nfrom llama_index import ListIndex, SimpleDirectoryReader, set_global_service_context\nfrom langchain.embeddings.huggingface import HuggingFaceEmbeddings\n# from llama_index import LangchainEmbedding, ServiceContext\nfrom langchain.embeddings.huggingface import HuggingFaceEmbeddings\nfrom llama_index import ServiceContext\n\nfrom transformers import pipeline\nimport time\nimport torch\nfrom langchain.llms.base import LLM\nfrom llama_index import LLMPredictor\n\nclass CustomLLM(LLM):\n    model_name = \"facebook/opt-iml-1.3b\"\n    # I am not using a GPU, but you can add device=\"cuda:0\"\n    # to the pipeline call if you have a local GPU or\n    # are running this on Google Colab:\n    pipeline = pipeline(\"text-generation\", model=model_name,\n                        model_kwargs={\"torch_dtype\":torch.bfloat16})\n\n    def _call(self, prompt, stop = None):\n        prompt_length = len(prompt)\n        response = self.pipeline(prompt, max_new_tokens=200)\n        first_response = response[0][\"generated_text\"]\n        # only return newly generated tokens\n        returned_text = first_response[prompt_length:]\n        return returned_text\n\n    @property\n    def _identifying_params(self):\n        return {\"name_of_model\": self.model_name}\n\n    @property\n    def _llm_type(self):\n        return \"custom\"\n\ntime1 = time.time()\n\n# define our LLM\nllm_predictor = LLMPredictor(llm=CustomLLM())\n\n# load in HF embedding model from langchain\nembed_model = HuggingFaceEmbeddings(model_name=\"sentence-transformers/all-mpnet-base-v2\")\nservice_context = ServiceContext.from_defaults(llm_predictor=llm_predictor, embed_model=embed_model)\nset_global_service_context(service_context)\nprint(\"Done creating service context\")\n\n# build index\ndocuments = SimpleDirectoryReader('../data').load_data()\nnew_index = ListIndex.from_documents(documents, service_context=service_context)\nprint(\"Done building index\")\n\n# query with embed_model specified\n#query_engine = new_index.as_query_engine(\n#    #retriever_mode=\"embedding\", \n#    retriever_mode=\"default\",\n#    response_mode = \"simple_summarize\",\n#    verbose=True, \n#    service_context=service_context\n#)\nquery_engine = new_index.as_query_engine()\n\nprint(\"Done creating query engine\")\n\ndef query(query_string):\n    response = query_engine.query(query_string)\n    print(response)\n    return response\n\nquery(\"what is the definition of Chemistry?\")\nquery(\"what are the benefits of sports?\")\nquery(\"why study economics?\")\n"
  },
  {
    "path": "hugging_face/throwaway_test.py",
    "content": "from llama_index import ListIndex, SimpleDirectoryReader\n#from llama_index import QueryEngine\n\n# Load documents from a directory\ndocuments = SimpleDirectoryReader('../data').load_data()\n\n# Create a new index from the documents\nnew_index = ListIndex.from_documents(documents)\n\n# Create a query engine from the new index\nquery_engine = new_index.as_query_engine()\n\n# Query the index\nresults = query_engine.query('what is the histry of economics?')\nprint(f\"results: {results}\")\n"
  },
  {
    "path": "kg_search/Google_KG_helper.py",
    "content": "\"\"\"Python client calling for Knowledge Graph Search API.\"\"\"\n\nimport json\nimport os\nfrom urllib.parse import urlencode\nfrom urllib.request import urlopen\nfrom pathlib import Path\nfrom pprint import pprint\n\n#api_key = open(str(Path.home()) + \"/.google_api_key\").read()\napi_key = os.environ.get(\"GOOGLE_API_KEY\")\n\n# use Google search API to get information about a named entity:\n\ndef get_entity_info(entity_name):\n    service_url = \"https://kgsearch.googleapis.com/v1/entities:search\"\n    params = {\n        \"query\": entity_name,\n        \"limit\": 1,\n        \"indent\": True,\n        \"key\": api_key,\n    }\n    url = service_url + \"?\" + urlencode(params)\n    response = json.loads(urlopen(url).read())\n    return response\n\ndef tree_traverse(a_dict):\n    ret = []\n    def recur(dict_2, a_list):\n        if isinstance(dict_2, dict):\n            for key, value in dict_2.items():\n                if key in ['name', 'description', 'articleBody']:\n                    a_list += [value]\n                recur(value, a_list)\n        if isinstance(dict_2, list):\n            for x in dict_2:\n                recur(x, a_list)\n    recur(a_dict, ret)\n    return ret\n\n\ndef get_context_text(entity_name):\n    json_data = get_entity_info(entity_name)\n    return ' '.join(tree_traverse(json_data))\n\nif __name__ == \"__main__\":\n    get_context_text(\"Bill Clinton\")\n"
  },
  {
    "path": "kg_search/Google_Knowledge_Graph_Search.py",
    "content": "\"\"\"Example of Python client calling Knowledge Graph Search API.\"\"\"\n\nfrom llama_index.core.schema import Document\nfrom llama_index.core import VectorStoreIndex\nimport Google_KG_helper\n\ndef kg_search(entity_name, *questions):\n    ret = \"\"\n    context_text = Google_KG_helper.get_context_text(entity_name)\n    print(f\"Context text: {context_text}\")\n    doc = Document(text=context_text)\n    index = VectorStoreIndex.from_documents([doc])\n    for question in questions:\n        response = index.as_query_engine().query(question)\n        ret += f\"QUESTION:  {question}\\nRESPONSE: {response}\\n\"\n    return ret\n\nif __name__ == \"__main__\":\n    print(kg_search(\"Bill Clinton\", \"When was Bill president?\"))\n"
  },
  {
    "path": "kg_search/README.md",
    "content": "# Running the example\n\nYou will need Google and OpenAI API keys (see book text).\n\nInstall virtualenv if required and set up a local environment:\n\n    pip install virtualenv  # if not already installed\n    python3 -m venv env     # create virtual env in current directory\n    source env/bin/activate # activate virtual environment\n    pip install -U llama-index\n    python Google_Knowledge_Graph_Search.py"
  },
  {
    "path": "kor/README.md",
    "content": "# Kor Library: a Useful Library for using LLMs to extract data from text\n\nThe Kor library was written by Eugene Yurtsev. Kor is useful for using LLMs to extract structured data from unstructured text. Kor works by generating appropriate prompt text to explain to GPT-3.5 what information to extract and adding in the text to be processed.\n\nThe [GitHub repository for Kor](https://github.com/eyurtsev/kor) is under active development so please check the project for updates. Here is the [documentation](https://eyurtsev.github.io/kor/).\n\n"
  },
  {
    "path": "kor/dates.py",
    "content": "\" From documentation: https://eyurtsev.github.io/kor/\"\n\nfrom kor.extraction import create_extraction_chain\nfrom kor.nodes import Object, Text, Number\nfrom langchain.chat_models import ChatOpenAI\nfrom pprint import pprint\nimport warnings ; warnings.filterwarnings('ignore')\n\nllm = ChatOpenAI(\n    model_name=\"gpt-3.5-turbo\",\n    temperature=0,\n    max_tokens=2000,\n    frequency_penalty=0,\n    presence_penalty=0,\n    top_p=1.0,\n)\n\nschema = Object(\n    id=\"date\",\n    description=(\n        \"Any dates found in the text. Should be output in the format:\"\n        \" January 12, 2023\"\n    ),\n    attributes = [\n        Text(id = \"month\",\n             description = \"The month of the date\",\n             examples=[(\"Someone met me on December 21, 1995\",\n                        \"Let's meet up on January 12, 2023 and discuss our yearly budget\")])\n    ],\n)\n\nchain = create_extraction_chain(llm, schema, encoder_or_encoder_class='json')\n\n\npred = chain.predict_and_parse(text=\"I will go to California May 1, 2024\")['data']\nprint(\"* month mentioned in text=\", pred)\n"
  },
  {
    "path": "langchain_dbpedia_agent/QA.py",
    "content": "# Copyright 2021-2023 Mark Watson\n# This is an example I wriote and distributed in 2021. Here I am just\n# reusing a few functions for my LangChain Agent book chapter.\n#\n# Original Colab notebook:\n# https://colab.research.google.com/drive/1FX-0eizj2vayXsqfSB2ONuJYG8BaYpGO?usp=sharing\n\n# !pip install import spacy\n# !python -m spacy download en_core_web_sm\n\nimport spacy\n\ntry:\n    nlp_model = spacy.load(\"en_core_web_sm\")\nexcept Exception as e:\n    print(f\"Fixing error: {e}\")\n    spacy.cli.download(\"en_core_web_sm\")\n    nlp_model = spacy.load(\"en_core_web_sm\")\n\nfrom SPARQLWrapper import SPARQLWrapper, JSON\n\nsparql = SPARQLWrapper(\"http://dbpedia.org/sparql\")\n\n\ndef query(query):\n    sparql.setQuery(query)\n    sparql.setReturnFormat(JSON)\n    return sparql.query().convert()[\"results\"][\"bindings\"]\n\n\ndef entities_in_text(s):\n    doc = nlp_model(s)\n    ret = {}\n    for [ename, etype] in [[entity.text, entity.label_] for entity in doc.ents]:\n        if etype in ret:\n            ret[etype] = ret[etype] + [ename]\n        else:\n            ret[etype] = [ename]\n    return ret\n\n\n# NOTE: !! note \"{{\" .. \"}}\" double curly brackets: this is to escape for Python String format method:\n\nsparql_query_template = \"\"\"\n     select distinct ?s ?comment where {{\n       ?s <http://www.w3.org/2000/01/rdf-schema#label>  '{name}'@en .\n       ?s <http://www.w3.org/2000/01/rdf-schema#comment>  ?comment  .\n       FILTER  (lang(?comment) = 'en') .\n       ?s <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> {dbpedia_type} .\n     }} limit 15\n\"\"\"\nprint(sparql_query_template)\n\n\ndef dbpedia_get_entities_by_name(name, dbpedia_type):\n    print(f\"{name=} {dbpedia_type=}\")\n    s_query = sparql_query_template.format(name=name, dbpedia_type=dbpedia_type)\n    print(s_query)\n    results = query(s_query)\n    return results\n\n\nentity_type_to_type_uri = {\n    \"PERSON\": \"<http://dbpedia.org/ontology/Person>\",\n    \"GPE\": \"<http://dbpedia.org/ontology/Place>\",\n    \"ORG\": \"<http://dbpedia.org/ontology/Organisation>\",\n}\n\n\ndef get_context_text(query_text):\n    entities = entities_in_text(query_text)\n\n    def helper(entity_type):\n        ret = \"\"\n        if entity_type in entities:\n            for hname in entities[entity_type]:\n                results = dbpedia_get_entities_by_name(\n                    hname, entity_type_to_type_uri[entity_type]\n                )\n                for result in results:\n                    ret += ret + result[\"comment\"][\"value\"] + \" . \"\n        return ret\n\n    context_text = helper(\"PERSON\") + helper(\"ORG\") + helper(\"GPE\")\n    #print(\"\\ncontext text:\\n\", context_text, \"\\n\")\n    return context_text\n\n"
  },
  {
    "path": "langchain_dbpedia_agent/README.md",
    "content": "# LangChain Agent Tool Example Using DBPedia SPARQL Queries\n\nTo run the example:\n\n    python custom_func_dbpedia.py\n\nExample output:\n\n```\n     select distinct ?s ?comment where {{\n       ?s <http://www.w3.org/2000/01/rdf-schema#label>  '{name}'@en .\n       ?s <http://www.w3.org/2000/01/rdf-schema#comment>  ?comment  .\n       FILTER  (lang(?comment) = 'en') .\n       ?s <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> {dbpedia_type} .\n     }} limit 15\n\n\n\n> Entering new AgentExecutor chain...\n\nInvoking: `get_context_data` with `{'query_text': 'Berlin'}`\n\n\nname='Berlin' dbpedia_type='<http://dbpedia.org/ontology/Place>'\n\n     select distinct ?s ?comment where {\n       ?s <http://www.w3.org/2000/01/rdf-schema#label>  'Berlin'@en .\n       ?s <http://www.w3.org/2000/01/rdf-schema#comment>  ?comment  .\n       FILTER  (lang(?comment) = 'en') .\n       ?s <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://dbpedia.org/ontology/Place> .\n     } limit 15\n\n\n{'context_text': \"Berlin (/bɜːrˈlɪn/ bur-LIN, German: [bɛʁˈliːn]) is the capital and largest city of Germany by both area and population. Its 3.6 million inhabitants make it the European Union's most populous city, according to population within city limits. One of Germany's sixteen constituent states, Berlin is surrounded by the State of Brandenburg and contiguous with Potsdam, Brandenburg's capital. Berlin's urban area, which has a population of around 4.5 million, is the second most populous urban area in Germany after the Ruhr. The Berlin-Brandenburg capital region has around 6.2 million inhabitants and is Germany's third-largest metropolitan region after the Rhine-Ruhr and Rhine-Main regions. . \"}\n\nBerlin is the capital and largest city of Germany. It is located in the northeastern part of the country. Berlin has a population of approximately 3.6 million people, making it the most populous city in the European Union. It is surrounded by the State of Brandenburg and is contiguous with Potsdam, the capital of Brandenburg. The urban area of Berlin has a population of around 4.5 million, making it the second most populous urban area in Germany after the Ruhr. The Berlin-Brandenburg capital region has a population of approximately 6.2 million, making it Germany's third-largest metropolitan region after the Rhine-Ruhr and Rhine-Main regions.\n\n> Finished chain.\n```\n"
  },
  {
    "path": "langchain_dbpedia_agent/custom_func_dbpedia.py",
    "content": "from QA import get_context_text\n\ndef get_context_data(query_text):\n    \"\"\"Method to get context text for entities from DBPedia using SPARQL query\"\"\"\n\n    query_text_data = get_context_text(query_text)\n    return {\"context_text\": query_text_data}\n\n\n## Custom function example using DBPedia\n\nfrom typing import Type\nfrom pydantic import BaseModel, Field\nfrom langchain.tools import BaseTool\n\n\nclass GetContextTextFromDbPediaInput(BaseModel):\n    \"\"\"Inputs for get_context_data\"\"\"\n\n    query_text: str = Field(description=\"query_text user supplied query text\")\n\n\nclass GetContextTextFromDbPediaTool(BaseTool):\n    name = \"get_context_data\"\n    description = \"\"\"\n        Useful when you want to make a query and get context text from DBPedia.\n        You should enter and text containing entity names\n        \"\"\"\n    args_schema: Type[BaseModel] = GetContextTextFromDbPediaInput\n\n    def _run(self, query_text: str):\n        text = get_context_data(query_text)\n        return text\n\n    def _arun(self, query_text: str):\n        raise NotImplementedError(\"get_context_data does not support async\")\n\n\n## Create agent\n\nfrom langchain.agents import AgentType\nfrom langchain.chat_models import ChatOpenAI\nfrom langchain.agents import initialize_agent\n\nllm = ChatOpenAI(model=\"gpt-4o-mini\", temperature=0)\n\ntools = [GetContextTextFromDbPediaTool()]\n\nagent = initialize_agent(tools, llm, agent=AgentType.OPENAI_FUNCTIONS, verbose=True)\n\n## Run agent\n\nagent.run(\n    \"What country is Berlin in and what other information about the city do you have?\"\n)\n"
  },
  {
    "path": "langchain_dbpedia_agent/requirements.txt",
    "content": "spacy==3.7.6\nSPARQLWrapper==2.0.0\nlangchain==0.2.14\nlangchain-community==0.2.12\nopenai==1.42.0"
  },
  {
    "path": "langchain_getting_started/.gitignore",
    "content": "*.log\ntmp\ntmp/*"
  },
  {
    "path": "langchain_getting_started/README.md",
    "content": "# LangChain Getting Started Examples\n\nRequirements:\n\n    pip install langchain langchain-openai faiss-cpu chromadb unstructured pdf2image pytesseract docx\n\nSee book for:\n\n    country_information.py\n    directions_template.py\n    doc_search.py\n\n## Examples not (yet) in my book\n\n    chroma_persist_index.py\n    chroma_use_disk_index_for_query.py\n\nNote: I sometines need to ***rm -rf ./tmp** when updating libraries to new versions.\n"
  },
  {
    "path": "langchain_getting_started/agent_test.py",
    "content": "from langchain.agents import initialize_agent, Tool\nfrom langchain.llms import OpenAI\n\nllm = OpenAI(temperature=0)\n\ndef add(input: str) -> str:\n    values = [int(x) for x in input.split(\"+\")]\n    return str(sum(values))\n\ndef is_prime(input: str) -> str:\n    n = int(input)\n\n    if n <= 1:\n        return \"no\"\n    if n <= 3:\n        return \"yes\"\n    if n % 2 == 0 or n % 3 == 0:\n        return \"no\"\n\n    i = 5\n    while i * i <= n:\n        if n % i == 0 or n % (i + 2) == 0:\n            return \"no\"\n        i += 6\n\n    return \"yes\"\n\ntools = [\n    Tool(\n        name = \"Add\",\n        func=add,\n        description=\"Useful for when you need to add numbers. Input should be in the form '1 + 2 + 3'.\"\n    ),\n    Tool(\n        name = \"IsPrime\",\n        func=is_prime,\n        description=\"Useful to know if a number is prime.\"\n    ),\n]\n\nagent = initialize_agent(tools, llm, agent=\"zero-shot-react-description\", verbose=True)\n\nprint(agent.run(\"Add the first 10 numbers and tell me if it's prime.\"))\n\n"
  },
  {
    "path": "langchain_getting_started/chroma_persist_index.py",
    "content": "from langchain.vectorstores import Chroma\nfrom langchain.embeddings.openai import OpenAIEmbeddings\n\nembeddings = OpenAIEmbeddings(model=\"text-embedding-3-large\")\nvectorstore = Chroma(collection_name=\"langchain_store\",\n                     embedding_function=embeddings,\n                     persist_directory=\"./tmp\")\n\n# Add data to the vector store\nvectorstore.add_texts(\n    [\"Chemicals are used in the production of many products. \",\n     \"The study of Physics is important for understanding the world around us.\",\n     \"Applications of Biology include the study of plants and animals.\"],\n     metadatas=[{\"source\": \"Mark\"}, {\"source\": \"Mark\"}, {\"source\": \"Mark\"}],\n     ids=[\"docC\", \"docP\", \"docB\"])\n\n# Persist the data to disk\nvectorstore.persist()\n\n"
  },
  {
    "path": "langchain_getting_started/chroma_use_disk_index_for_query.py",
    "content": "from langchain.vectorstores import Chroma\nfrom langchain.embeddings.openai import OpenAIEmbeddings\n\nembeddings = OpenAIEmbeddings(model=\"text-embedding-3-large\")\nvectorstore = Chroma(collection_name=\"langchain_store\",\n                     embedding_function=embeddings,\n                     persist_directory=\"./tmp\")\n\n# Query the vector store\nresults = vectorstore.similarity_search(\"the red chemical\", k=1)\nprint(results)\n"
  },
  {
    "path": "langchain_getting_started/country_information.py",
    "content": "from langchain.prompts import PromptTemplate\nfrom langchain_openai import OpenAI\nllm = OpenAI(temperature=0.9)\n\ndef get_country_information(country_name):\n    print(f\"\\nProcessing {country_name}:\")\n    global prompt\n    if \"prompt\" not in globals():\n        print(\"Creating prompt...\")\n        prompt = PromptTemplate(\n            input_variables=[\"country_name\"],\n            template = \"\"\"\nPredict the capital and population of a country.\n\nCountry: {country_name}\nCapital:\nPopulation:\"\"\",\n        )\n    prompt_text = prompt.format(country_name=country_name)\n    print(prompt_text)\n    return llm(prompt_text)\n\nprint(get_country_information(\"Canada\"))\nprint(get_country_information(\"Germany\"))"
  },
  {
    "path": "langchain_getting_started/directions_template.py",
    "content": "from langchain.prompts import PromptTemplate\nfrom langchain_openai import OpenAI\nllm = OpenAI(temperature=0.9)\n\ndef get_directions(thing_to_do):\n    prompt = PromptTemplate(\n        input_variables=[\"thing_to_do\"],\n        template=\"How do I {thing_to_do}?\",\n    )\n    prompt_text = prompt.format(thing_to_do=thing_to_do)\n    print(f\"\\n{prompt_text}:\")\n    return llm(prompt_text)\n\nprint(get_directions(\"get to the store\"))\nprint(get_directions(\"hang a picture on the wall\"))\n"
  },
  {
    "path": "langchain_getting_started/doc_search.py",
    "content": "from langchain_community.vectorstores import FAISS\nfrom langchain_core.output_parsers import StrOutputParser\nfrom langchain_core.prompts import ChatPromptTemplate\nfrom langchain_core.runnables import RunnablePassthrough\nfrom langchain_openai import ChatOpenAI, OpenAIEmbeddings\nfrom langchain_community.document_loaders import DirectoryLoader\n\nmodel = ChatOpenAI()\n\nfrom read_text_files import read_text_files\n\nvectorstore = FAISS.from_texts(read_text_files(\"../data/\"), embedding=OpenAIEmbeddings())\n\nretriever = vectorstore.as_retriever()\n\ntemplate = \"\"\"Answer the question based only on the following context:\n{context}\n\nQuestion: {question}\n\"\"\"\nprompt = ChatPromptTemplate.from_template(template)\n\nchain = (\n    {\"context\": retriever, \"question\": RunnablePassthrough()}\n    | prompt\n    | model\n    | StrOutputParser()\n)\n\nprint(chain.invoke(\"who tried to define what Chemistry is?\"))\n\nprint(chain.invoke(\"What kinds of equipment are in a chemistry laboratory?\"))\nprint(chain.invoke(\"What is Austrian School of Economics?\"))\nprint(chain.invoke(\"Why do people engage in sports?\"))\nprint(chain.invoke(\"What is the effect of body chemistry on exercise?\"))"
  },
  {
    "path": "langchain_getting_started/read_text_files.py",
    "content": "import os\n\n# Generated by Perplexity\n\ndef read_text_files(directory_path):\n    \"\"\"\n    Reads all .txt files in the specified directory and returns their contents as a list of strings.\n\n    :param directory_path: The path to the directory containing .txt files\n    :return: A list of strings where each string is the content of a .txt file\n    \"\"\"\n    txt_contents = []\n    # Check if the directory exists\n    if not os.path.isdir(directory_path):\n        print(f\"The directory {directory_path} does not exist.\")\n        return txt_contents\n\n    # Iterate over all files in the directory\n    for filename in os.listdir(directory_path):\n        # Check for .txt extension\n        if filename.endswith(\".txt\"):\n            # Construct full file path\n            file_path = os.path.join(directory_path, filename)\n            # Open and read the contents of the file\n            try:\n                with open(file_path, 'r') as file:\n                    txt_contents.append(file.read())\n            except IOError as e:\n                print(f\"Failed to read file {filename}: {e}\")\n\n    return txt_contents\n"
  },
  {
    "path": "langchain_getting_started/test.py",
    "content": "from langchain_community.vectorstores import FAISS\nfrom langchain_core.output_parsers import StrOutputParser\nfrom langchain_core.prompts import ChatPromptTemplate\nfrom langchain_core.runnables import RunnablePassthrough\nfrom langchain_openai import ChatOpenAI, OpenAIEmbeddings\nfrom langchain_community.document_loaders import DirectoryLoader\n\nmodel = ChatOpenAI()\n\nfrom read_text_files import read_text_files\n\nvectorstore = FAISS.from_texts(read_text_files(\"../data/\"), embedding=OpenAIEmbeddings())\n\nretriever = vectorstore.as_retriever()\n\ntemplate = \"\"\"Answer the question based only on the following context:\n{context}\n\nQuestion: {question}\n\"\"\"\nprompt = ChatPromptTemplate.from_template(template)\n\nchain = (\n    {\"context\": retriever, \"question\": RunnablePassthrough()}\n    | prompt\n    | model\n    | StrOutputParser()\n)\nprint(chain.invoke(\"who tried to define what Chemistry is?\"))\n\n\n\n\nexit(0)\n\nfrom langchain_community.document_loaders import TextLoader, DirectoryLoader\n#from langchain_community.embeddings.sentence_transformer import (\n#    SentenceTransformerEmbeddings,\n#)\nfrom langchain_openai import OpenAIEmbeddings\nfrom langchain_community.vectorstores import FAISS\nfrom langchain_text_splitters import RecursiveCharacterTextSplitter\nfrom langchain.chains import VectorDBQA\nfrom langchain_community.llms import OpenAI\nfrom langchain_openai import OpenAIEmbeddings\n\nembeddings = OpenAIEmbeddings()\n\n#loader = TextLoader(\"../data/chemistry.txt\", \"../data/health.txt\", show_progress=True)\nloader = DirectoryLoader('../data', glob=\"**/*.txt\", show_progress=False)\ndocuments = loader.load()\nprint(\"documents:\", documents)\n\ntext_splitter = RecursiveCharacterTextSplitter()\ndocuments = text_splitter.split_documents(documents)\nprint(\"documents:\", documents)\nvector = FAISS.from_documents(documents, embeddings)\nprint(dir(vector))\nexit(0)\n\n# split it into chunks\ntext_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)\ndocs = text_splitter.split_documents(documents)\n\ntexts = text_splitter.split_documents(documents)\n\ndocsearch = Chroma.from_documents(texts, OpenAIEmbeddings())\n\nqa = VectorDBQA.from_chain_type(llm=OpenAI(),\n                                chain_type=\"stuff\",\n                                vectorstore=docsearch)\n\ndef query(q):\n    print(f\"Query: {q}\")\n    print(f\"Answer: {qa.run(q)}\")\n\nquery(\"What kinds of equipment are in a chemistry laboratory?\")\nquery(\"What is Austrian School of Economics?\")\nquery(\"Why do people engage in sports?\")\nquery(\"What is the effect of body chemistry on exercise?\")\n\nexit(0)\ndb = Chroma.from_documents(docs, SentenceTransformerEmbeddings())\n\n# query it\nquery = \"What is the definition of chemistry?\"\ndocs = db.similarity_search(query)\n\n# print results\nprint(docs[0].page_content)\n\n\n\n\n\nexit(0)\nfrom langchain.llms import OpenAI\n#from langchain.document_stores import InMemoryDocumentStore\nfrom langchain_community.vectorstores import Chroma\n\nimport os\n\n# Initialize LangChain components\nllm = OpenAI()\n#document_store = InMemoryDocumentStore()\ndocument_store = Chroma()\n\n# Directory containing text files\ntext_files_directory = \"../data/\"\n\n# Read text files and add them to the document store\nfor filename in os.listdir(text_files_directory):\n    if filename.endswith(\".txt\"):\n        with open(os.path.join(text_files_directory, filename), 'r', encoding='utf-8') as file:\n            text_content = file.read()\n            document_store.add_documents([{\"text\": text_content, \"metadata\": {\"filename\": filename}}])\n\n# Example query\nquery = \"What is the main topic discussed in the documents?\"\nresponse = llm.query_document_store(query, document_store=document_store)\n\nprint(\"Query Result:\", response)\n"
  },
  {
    "path": "llama-index_case_study/.gitignore",
    "content": "index.json\ncache\n\n"
  },
  {
    "path": "llama-index_case_study/README.md",
    "content": "# LlamaIndex Examples\n\nThe old example test_from_docs.py has been deprecated and removed. The new example is web_page_QA.py\n"
  },
  {
    "path": "llama-index_case_study/requirements.txt",
    "content": "llama_index\ntrafilatura"
  },
  {
    "path": "llama-index_case_study/web_page_QA.py",
    "content": "# Derived from examples in llama_index documentation\n\n# pip install llama-index html2text trafilatura\n\nfrom pprint import pprint\nfrom llama_index.core import Document\nimport trafilatura\n\nfrom llama_index.core import VectorStoreIndex\n\ndef query_website(url, *questions):\n    downloaded = trafilatura.fetch_url(url)\n    text = trafilatura.extract(downloaded)\n    #print(text)\n    list_of_documents = [Document(text=text)]\n    index = VectorStoreIndex.from_documents(list_of_documents)   #.from_texts([text])\n    engine = index.as_query_engine()\n    for question in questions:\n        print(f\"\\n== QUESTION: {question}\\n\")\n        response = engine.query(question)\n        print(f\"== RESPONSE: {response}\")\n\nif __name__ == \"__main__\":\n  url = \"https://markwatson.com\"\n  query_website(url, \"What instruments does Mark play?\",\n                     \"How many books has Mark written?\",\n                     \"list company names beginning with the letter 'C'\")\n"
  },
  {
    "path": "llama.cpp/README.md",
    "content": "# llama.cpp local modes with Langchain\n\nreference:\n\nhttps://python.langchain.com/docs/integrations/llms/llamacpp\n\n"
  },
  {
    "path": "llama.cpp/test.py",
    "content": "# code example from:\n#   https://python.langchain.com/docs/integrations/llms/llamacpp\n\nfrom langchain.llms import LlamaCpp\nfrom langchain.prompts import PromptTemplate\nfrom langchain.chains import LLMChain\nfrom langchain.callbacks.manager import CallbackManager\nfrom langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler\n\ntemplate = \"\"\"Question: {question}\n\nAnswer: Let's work this out in a step by step way to be sure we have the right answer.\"\"\"\n\nprompt = PromptTemplate(template=template, input_variables=[\"question\"])\n\n# Callbacks support token-wise streaming\ncallback_manager = CallbackManager([StreamingStdOutCallbackHandler()])\n\n# Make sure the model path is correct for your system!\nllm = LlamaCpp(\n    model_path=\"/Users/markw/llama.cpp/models/openassistant-llama2-13b-orca-8k-3319.Q5_K_M.gguf\",\n    temperature=0.75,\n    max_tokens=2000,\n    top_p=1,\n    callback_manager=callback_manager, \n    verbose=True, # Verbose is required to pass to the callback manager\n)\n\nprompt = \"\"\"\nQuestion: If Mary is 30 years old and Bob is 25, who is older and by how much?\n\"\"\"\nprint(llm(prompt))\n"
  },
  {
    "path": "llm_enhanced_search_ddg_ollama/README.md",
    "content": "# Multi-prompt Search using LLMs, the Duckduckgo search API, and local Ollama models\n\n\n## install Duckduckgo search API library\n\ndocs:\n\nhttps://pypi.org/project/duckduckgo-search/\n\n    pip install duckduckgo_search_api\n\nI believe the Duckduckgo search API can only be used for low volume non-commercial use. I find the results very useful because they are served from Duck Duck Go's internal quick-look data.\n\nAlso install:\n\n    pip install llama-index html2text trafilatura"
  },
  {
    "path": "llm_enhanced_search_ddg_ollama/llm_enhanced_search.py",
    "content": "from ddg import Duckduckgo\nfrom langchain_community.llms.ollama import Ollama\n\n# pip install llama-index html2text trafilatura\nimport trafilatura\n\nfrom pprint import pprint\n\nddg_api = Duckduckgo()\n\n\nllm = Ollama(\n    model=\"mistral:v0.3\",\n    verbose=False,\n    #callback_manager=CallbackManager([StreamingStdOutCallbackHandler()]),\n)\n\nprompt1 = \"return concisely either 'Y' or 'N' if this query | %s | is matched well by the following text: %s\"\nprompt2 = \"Using the query | %s | summarize the following text including only material relevant to the query:\\n%s\"\nprompt3 = \"Using the query | %s | summarize in multiple paragraphs the following text including only material relevant to the query:\\n%s\"\n\ndef llm_search(query):\n    results = ddg_api.search(query)\n    data = results['data']\n    good_results = []\n    good_summaries = []\n    for d in data:\n        description = d['description']\n        p = prompt1 % (query, description)\n        s = llm.invoke(p)\n        print(f\"Prompt: {p}\\nResponse: {s}\\n\\n\")\n        if s.strip()[0:1] == 'Y':\n            good_results.append(d)\n            uri = d['url']\n            downloaded = trafilatura.fetch_url(uri)\n            text = trafilatura.extract(downloaded)\n            p2 = prompt2 % (query, text)\n            s2 = llm.invoke(p2)\n            good_summaries.append(s2)\n    p3 = prompt3 % (query, \"\\n\\n\".join(good_summaries))\n    final_summary = llm.invoke(p3)\n\n    return (good_results, good_summaries, final_summary)\n\ndef test1():\n    (results, summaries, final_summary) = llm_search(\"Common Lisp and Deep Learning consultant\")\n\n    print(f\"\\n\\n****** Good Results ******\\n\\n\")\n    print(results)\n\n    print(f\"\\n\\n****** Good Summaries ******\\n\\n\")\n    print(summaries)\n\n    print(f\"\\n\\n****** Final Summary ******\\n\\n\")\n    print(final_summary)\n\ntest1()\n\ndef test2():\n    (results, summaries, final_summary) = llm_search(\"Write a business plan for a new startup using LLMs and expertise in medical billing.\")\n\n    print(f\"\\n\\n****** Good Results ******\\n\\n\")\n    print(results)\n\n    print(f\"\\n\\n****** Good Summaries ******\\n\\n\")\n    print(summaries)\n\n    print(f\"\\n\\n****** Final Summary ******\\n\\n\")\n    print(final_summary)\n\n# test2()"
  },
  {
    "path": "neo4j/README.md",
    "content": "# Neo4J example, not yet in my book, this directory just shows an example from LlamaIndex docs\n\ndocs with this example:\n\n    https://llamahub.ai/l/llama_packs-neo4j_query_engine\n"
  },
  {
    "path": "neo4j/credentials.json",
    "content": "{\"username\": \"neo4j\", \"password\": \"demodemo\", \"database\": \"llamatest\", \"url\": \"bolt://localhost:7687\"}\n\n"
  },
  {
    "path": "neo4j/test_example.py",
    "content": "import json\n\nfrom llama_index.llama_pack import download_llama_pack\n\n# download and install dependencies\nNeo4jQueryEnginePack = download_llama_pack(\n  \"Neo4jQueryEnginePack\", \"./neo4j_pack\"\n)\n\n# Load the docs (example of Paleo diet from Wikipedia)\nfrom llama_index import download_loader\n\nWikipediaReader = download_loader(\"WikipediaReader\")\nloader = WikipediaReader()\ndocs = loader.load_data(pages=['Paleolithic diet'], auto_suggest=False)\nprint(f'Loaded {len(docs)} documents')\n\n# get Neo4j credentials (assume it's stored in credentials.json)\nwith open('credentials.json') as f:\n  neo4j_connection_params = json.load(f)\n  username = neo4j_connection_params['username']\n  password = neo4j_connection_params['password']\n  url = neo4j_connection_params['url']\n  database = neo4j_connection_params['database']\n\nprint(f'username: {username}', f'password: {password}', f'url: {url}', f'database: {database}')\n# create the pack\nneo4j_pack = Neo4jQueryEnginePack(\n  username = username,\n  password = password,\n  url = url,\n  database = database,\n  docs = docs\n)\n\nresponse = neo4j_pack.run(\"Tell me about the benefits of paleo diet.\")\nprint(f\"{response}\\n\\n\")\n\n\nresponse = neo4j_pack.run(\"What kinds of food should I buy for a paleo diet.\")\nprint(f\"{response}\")\n"
  },
  {
    "path": "ollama_langchain/README.md",
    "content": ""
  },
  {
    "path": "ollama_langchain/llama3-instruct-8b.py",
    "content": "# requires \"ollama serve\" to be running in another terminal\n\nfrom langchain_community.llms.ollama import Ollama\n\nllm = Ollama(\n    model=\"llama3:instruct\",\n    verbose=True,\n)\n\n#s = llm(\"how much is 1 + 2?\")\n#print(s)\n\ns = llm.invoke(\"If Sam is 27, Mary is 42, and Jerry is 33, what are their age differences? Be concise\")\nprint(s)"
  },
  {
    "path": "ollama_langchain/mistral-7b.py",
    "content": "# requires \"ollama serve\" to be running in another terminal\n\nfrom langchain_community.llms.ollama import Ollama\n\nllm = Ollama(\n    model=\"mistral-nemo:latest\",\n    verbose=False,\n)\n\ns = llm.invoke(\"how much is 1 + 2?\")\nprint(s)\n\ns = llm.invoke(\"If Sam is 27, Mary is 42, and Jerry is 33, what are their age differences?\")\nprint(s)"
  },
  {
    "path": "ollama_langchain/rag_test.py",
    "content": "# requires \"ollama serve\" to be running in another terminal\n\n# pip install python-docx\n\n\nfrom langchain_community.llms.ollama import Ollama\nfrom langchain_community.embeddings import OllamaEmbeddings\nfrom langchain.chains import RetrievalQA\n\nfrom langchain_community.vectorstores import Chroma\nfrom langchain.text_splitter import RecursiveCharacterTextSplitter\nfrom langchain_community.document_loaders import DirectoryLoader\n\nmodel = \"mistral:v0.3\"\n\n# Create index (can be reused):\n\nloader = DirectoryLoader('../data/', glob=\"**/*.txt\", show_progress=True)\ndata = loader.load()\n\ntext_splitter = RecursiveCharacterTextSplitter(\n    chunk_size=100, chunk_overlap=100)\nall_splits = text_splitter.split_documents(data)\n\npersist_directory = 'cache'\n\nvectorstore = Chroma.from_documents(\n    documents = all_splits,\n    embedding = OllamaEmbeddings(model=model),\n    persist_directory=persist_directory)\n\nvectorstore.persist()\n\n# Try reloading index from disk and using for search:\n\npersist_directory = 'cache'\n\nvectorstore = Chroma(persist_directory=persist_directory,\n                     embedding_function=OllamaEmbeddings(model=model)\n                    )\n\nllm = Ollama(base_url = \"http://localhost:11434\",\n             model = model,\n             verbose = False,\n            )\n\nretriever = vectorstore.as_retriever()\n\nqa_chain = RetrievalQA.from_chain_type(\n            llm = llm,\n            chain_type = 'stuff',\n            retriever = retriever,\n            verbose = True,)\n\nwhile True:\n    query = input(\"Ask a question: \")\n    response = qa_chain(query)\n    print(response['result'])\n"
  },
  {
    "path": "ollama_langchain/requirements.txt",
    "content": "langchain_community\nlangchain\n"
  },
  {
    "path": "prompt_examples/two-shot-2-var.txt",
    "content": "Given the two examples below, extract the names, addresses, and email addresses of individuals mentioned later as Process Text. Format the extracted information in JSON, with keys for \"name\", \"address\", and \"email\". If any information is missing, use \"null\" for that field. Be concise in your output by providing only the output JSON.\n\nExample 1:\nText: \"John Doe lives at 1234 Maple Street, Springfield. His email is johndoe@example.com.\"\nOutput: \n{\n  \"name\": \"John Doe\",\n  \"address\": \"1234 Maple Street, Springfield\",\n  \"email\": \"johndoe@example.com\"\n}\n\nExample 2:\nText: \"Jane Smith has recently moved to 5678 Oak Avenue, Anytown. She hasn't updated her email yet.\"\nOutput: \n{\n  \"name\": \"Jane Smith\",\n  \"address\": \"5678 Oak Avenue, Anytown\",\n  \"email\": null\n}\n\nProcess Text: \"{input_text}\"\nOutput:\n"
  },
  {
    "path": "prompt_examples/two-shot-2.txt",
    "content": "Given the two examples below, extract the names, addresses, and email addresses of individuals mentioned later as Process Text. Format the extracted information in JSON, with keys for \"name\", \"address\", and \"email\". If any information is missing, use \"null\" for that field. Be concise in your output by providing only the output JSON.\n\nExample 1:\nText: \"John Doe lives at 1234 Maple Street, Springfield. His email is johndoe@example.com.\"\nOutput: \n{\n  \"name\": \"John Doe\",\n  \"address\": \"1234 Maple Street, Springfield\",\n  \"email\": \"johndoe@example.com\"\n}\n\nExample 2:\nText: \"Jane Smith has recently moved to 5678 Oak Avenue, Anytown. She hasn't updated her email yet.\"\nOutput: \n{\n  \"name\": \"Jane Smith\",\n  \"address\": \"5678 Oak Avenue, Anytown\",\n  \"email\": null\n}\n\nProcess Text: \"Mark Johnson enjoys living in Berkeley California at 102 Dunston Street and use mjess@foobar.com for contacting him.\"\nOutput:\n"
  },
  {
    "path": "rag/README.md",
    "content": "# RAG examples\n\nFor my book [LangChain and LlamaIndex Projects Lab Book: Hooking Large Language Models Up to the Real World](https://leanpub.com/langchain/read) (link to read free online)\n\n    pip install -U llama-index \n    pip install torch sentence-transformers # for reranking example"
  },
  {
    "path": "rag/requirements.txt",
    "content": "llama-index==0.11.1\nsentence-transformers==3.0.1\n"
  },
  {
    "path": "rag/reranking_rag_llama_index.py",
    "content": "from llama_index.core import VectorStoreIndex, SimpleDirectoryReader\nfrom llama_index.embeddings.openai import OpenAIEmbedding\nfrom llama_index.core.node_parser import SentenceSplitter\nfrom llama_index.core.extractors import TitleExtractor\nfrom llama_index.core.ingestion import IngestionPipeline\nfrom llama_index.core.postprocessor import SentenceTransformerRerank\n\n# Set up the ingestion pipeline with transformations\npipeline = IngestionPipeline(\n    transformations=[\n        SentenceSplitter(chunk_size=25, chunk_overlap=0),\n        TitleExtractor(),\n        OpenAIEmbedding(),\n    ]\n)\n\n# Load documents using a directory reader\ndocuments = SimpleDirectoryReader(\"../data\").load_data()\n\n# Create an index from the documents\nindex = VectorStoreIndex.from_documents(documents)\n\n# Initialize the reranker with a specific model\nreranker = SentenceTransformerRerank(\n    model=\"cross-encoder/ms-marco-MiniLM-L-12-v2\",  # Example model, adjust as needed\n    top_n=3  # Adjust the number of top documents to rerank\n)\n\n# Set up the query engine with the reranker as a postprocessor\nquery_engine = index.as_query_engine(\n    similarity_top_k=10,  # Set for how many results to retrieve before reranking\n    node_postprocessors=[reranker]  # Add the reranker to the postprocessing steps\n)\n\n# Perform a query\n#response = query_engine.query(\"List a few sports\")\nresponse = query_engine.query(\"Compare sports with the study of health issues\")\n\n# Print the response\nprint(response)"
  },
  {
    "path": "rag/simple_llama_index_retrieve_docs.py",
    "content": "from llama_index.core import VectorStoreIndex\nfrom llama_index.core import Document\n\ntext_list = [\"LlamaIndex is a powerful tool for LLM applications.\",\n             \"It helps in structuring and retrieving data efficiently.\"]\ndocuments = [Document(text=t) for t in text_list]\n\nindex = VectorStoreIndex.from_documents(documents)\nquery_engine = index.as_query_engine()\n\nretrieved_docs = query_engine.retrieve(\"What is LlamaIndex?\")\nprint(retrieved_docs)\n"
  },
  {
    "path": "rag/simple_rag_llama_index.py",
    "content": "from llama_index.core import VectorStoreIndex, SimpleDirectoryReader\nfrom llama_index.embeddings.openai import OpenAIEmbedding\nfrom llama_index.core.node_parser import SentenceSplitter\nfrom llama_index.core.extractors import TitleExtractor\nfrom llama_index.core.ingestion import IngestionPipeline\n\npipeline = IngestionPipeline(\n    transformations=[\n        SentenceSplitter(chunk_size=25, chunk_overlap=0),\n        TitleExtractor(),\n        OpenAIEmbedding(),\n    ]\n)\n\ndocuments = SimpleDirectoryReader(\"../data_small\").load_data()\nindex = VectorStoreIndex.from_documents(documents)\n\nquery_engine = index.as_query_engine()\nresponse = query_engine.query(\"List a few sports\")\n\nprint(response)\n"
  },
  {
    "path": "requirements.txt",
    "content": "llama-index>=0.10.13\n\n"
  },
  {
    "path": "sqlite/README.md",
    "content": "# SqLite Chat Example\n\nWe will use the SQlite sample database from the SQLite Tutorial web site:\n\n    https://www.sqlitetutorial.net/sqlite-sample-database/\n\nThis database has 11 tables. The about URI has documentatiuon for this database so please take a minute to review the [table schema diagram and text description](https://www.sqlitetutorial.net/sqlite-sample-database/).\n\n\n"
  },
  {
    "path": "sqlite/requirements.txt",
    "content": "langchain==0.3.3\nlangchain_community==0.3.2\nlangchain_experimental==0.3.3\nopenai==1.51.2"
  },
  {
    "path": "sqlite/sqlite_chat_test.py",
    "content": "from langchain.llms import OpenAI\nfrom langchain_experimental.sql import SQLDatabaseChain\nfrom langchain_experimental.sql.base import SQLDatabase\n\n# Initialize the database and LLM\ndb = SQLDatabase.from_uri(\"sqlite:///chinook.db\")\nllm = OpenAI(temperature=0)\n\n# Create a SQLDatabaseChain\ndb_chain = SQLDatabaseChain.from_llm(llm=llm, db=db, verbose=True)\n\n# Run queries\ndb_chain.run(\"How many employees are there?\")\ndb_chain.run(\"What is the name of the first employee?\")\ndb_chain.run(\"Which customer has the most invoices?\")\ndb_chain.run(\"List all music genres in the database\")"
  },
  {
    "path": "summarization/README.md",
    "content": "# Text summarization\n\n"
  },
  {
    "path": "summarization/prompt.txt",
    "content": "Summarize the following text: \"{input_text}\"\nOutput:\n"
  },
  {
    "path": "summarization/requirements.txt",
    "content": "openai\n"
  },
  {
    "path": "summarization/summarization_example.py",
    "content": "import openai\nfrom openai import OpenAI\nimport os\n\nopenai.api_key = os.getenv(\"OPENAI_API_KEY\")\nclient = OpenAI()\n\n# Read the prompt from a text file\nwith open('prompt.txt', 'r') as file:\n    prompt_template = file.read()\n\n# Substitute a string variable into the prompt\nwith open('../data/economics.txt', 'r') as file:\n    input_text = file.read()\nprompt = prompt_template.replace(\"input_text\", input_text)\n\n# Use the OpenAI completion API to generate a response with GPT-4\ncompletion = client.chat.completions.create(\n    model=\"gpt-4o-mini\",\n    messages=[\n        {\n            \"role\": \"user\",\n            \"content\": prompt,\n        },\n    ],\n)\n\nprint(completion.choices[0].message.content)\n"
  },
  {
    "path": "tavily_search/READMS.md",
    "content": "Example code from:\n\nLangChain: https://docs.tavily.com/docs/tavily-api/langchain\n\nLlamaIndex: https://docs.tavily.com/docs/tavily-api/llamaindex\n"
  },
  {
    "path": "tavily_search/langchain_test.py",
    "content": "import os\nfrom langchain.utilities.tavily_search import TavilySearchAPIWrapper\nfrom langchain.agents import initialize_agent, AgentType\nfrom langchain_openai import ChatOpenAI\nfrom langchain.tools.tavily_search import TavilySearchResults\n\n# set TAVILY_API_KEY environment variable\n\n# set up the agent\nllm = ChatOpenAI(model_name=\"gpt-4\", temperature=0.5)\nsearch = TavilySearchAPIWrapper()\ntavily_tool = TavilySearchResults(api_wrapper=search)\n\n# initialize the agent\nagent_chain = initialize_agent(\n    [tavily_tool],\n    llm,\n    agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,\n    verbose=True,\n)\n\n# run the agent\nprint(agent_chain.run(\"What are fun things to do in Sedona Arizona?\"))"
  },
  {
    "path": "tavily_search/llamaindex_test.py",
    "content": "from llama_hub.tools.tavily_research import TavilyToolSpec\nfrom llama_index.agent import OpenAIAgent\nimport os\n\n# set TAVILY_API_KEY environment variable\n\ntavily_tool = TavilyToolSpec(api_key=os.environ.get(\"TAVILY_API_KEY\"))\n\nagent = OpenAIAgent.from_tools(tavily_tool.to_tool_list())\n\nprint(agent.chat(\"What are fun things to do in Sedona Arizona?\"))"
  },
  {
    "path": "text_db_llm/README.md",
    "content": "# Text Database Supporting Search and Chat-based Exploration\n\nTBD\n\n\n"
  },
  {
    "path": "text_db_llm/data/chemistry.txt",
    "content": "Amyl alcohol is an organic compound with the formula C 5 H 12 O. All eight isomers of amyl alcohol are known. The most important is isobutyl carbinol, this being the chief constituent of fermentation amyl alcohol, and consequently a constituent of fusel oil. It can be separated from fusel oil by shaking with strong brine , separating the oily layer from the brine layer and it, the portion boiling between 125 and 140 °C. being collected. For further purification it may be shaken with hot lime water, the oily layer separated, dried with calcium chloride and fractionated, the fraction boiling between 128 and 132 °C only being collected.\nThe 1730 definition of the word \"chemistry\", as used by Georg Ernst Stahl, meant the art of resolving mixed, compound, or aggregate bodies into their principles; and of composing such bodies from those principles.[15] In 1837, Jean-Baptiste Dumas considered the word \"chemistry\" to refer to the science concerned with the laws and effects of molecular forces.[16] This definition further evolved until, in 1947, it came to mean the science of substances: their structure, their properties, and the reactions that change them into other substances - a characterization accepted by Linus Pauling.[17] More recently, in 1998, the definition of \"chemistry\" was broadened to mean the study of matter and the changes it undergoes, as phrased by Professor Raymond Chang.\nThe current model of atomic structure is the quantum mechanical model.[36] Traditional chemistry starts with the study of elementary particles, atoms, molecules,[37] substances, metals, crystals and other aggregates of matter. This matter can be studied in solid, liquid, or gas states, in isolation or in combination. The interactions, reactions and transformations that are studied in chemistry are usually the result of interactions between atoms, leading to rearrangements of the chemical bonds which hold atoms together. Such behaviors are studied in a chemistry laboratory.\n\nThe chemistry laboratory stereotypically uses various forms of laboratory glassware. However glassware is not central to chemistry, and a great deal of experimental (as well as applied/industrial) chemistry is done without it.\nThe transfer of energy from one chemical substance to another depends on the size of energy quanta emitted from one substance. However, heat energy is often transferred more easily from almost any substance to another because the phonons responsible for vibrational and rotational energy levels in a substance have much less energy than photons invoked for the electronic energy transfer. Thus, because vibrational and rotational energy levels are more closely spaced than electronic energy levels, heat is more easily transferred between substances relative to light or other forms of electronic energy. For example, ultraviolet electromagnetic radiation is not transferred with as much efficacy from one substance to another as thermal or electrical energy.\n"
  },
  {
    "path": "text_db_llm/data/economics.txt",
    "content": "The Austrian School (also known as the Vienna School or the Psychological School ) is a Schools of economic thought|school of economic thought that emphasizes the spontaneous organizing power of the price mechanism. Austrians hold that the complexity of subjective human choices makes mathematical modelling of the evolving market extremely difficult (or Undecidable and advocate a \"laissez faire\" approach to the economy. Austrian School economists advocate the strict enforcement of voluntary contractual agreements between economic agents, and hold that commercial transactions should be subject to the smallest possible imposition of forces they consider to be (in particular the smallest possible amount of government intervention). The Austrian School derives its name from its predominantly Austrian founders and early supporters, including Carl Menger, Eugen von Böhm-Bawerk and Ludwig von Mises.\n\nEconomics is the social science that analyzes the production, distribution, and consumption of goods and services.  Political economy was the earlier name for the subject, but economists in the late 19th century suggested \"economics\" as a shorter term for \"economic science\" that also avoided a narrow political-interest connotation and as similar in form to \"mathematics\", \"ethics\", and so forth.[2]\n\nA focus of the subject is how economic agents behave or interact and how economies work. Consistent with this, a primary textbook distinction is between microeconomics and macroeconomics. Microeconomics examines the behavior of basic elements in the economy, including individual agents (such as households and firms or as buyers and sellers) and markets, and their interactions. Macroeconomics analyzes the entire economy and issues affecting it, including unemployment, inflation, economic growth, and monetary and fiscal policy.\n\n\t\tThe professionalization of economics, reflected in the growth of graduate programs on the subject, has been described as \"the main change in economics since around 1900\".[93] Most major universities and many colleges have a major, school, or department in which academic degrees are awarded in the subject, whether in the liberal arts, business, or for professional study; see Master of Economics.\n\n\t\t\n\t\tEconomics is the social science that studies the behavior of individuals, households, and organizations (called economic actors, players, or agents), when they manage or use scarce resources, which have alternative uses, to achieve desired ends. Agents are assumed to act rationally, have multiple desirable ends in sight, limited resources to obtain these ends, a set of stable preferences, a definite overall guiding objective, and the capability of making a choice. There exists an economic problem, subject to study by economic science, when a decision (choice) is made by one or more resource-controlling players to attain the best possible outcome under bounded rational conditions. In other words, resource-controlling agents maximize value subject to the constraints imposed by the information the agents have, their cognitive limitations, and the finite amount of time they have to make and execute a decision. Economic science centers on the activities of the economic agents that comprise society.[1] They are the focus of economic analysis.[2]\n\n\t\tThe traditional concern of economic analysis is to gain an understanding of the processes that govern the production, distribution and consumption of goods and services in an exchange economy.[3] An approach to understanding these processes, through the study of agent behavior under scarcity, may go as   s:\n"
  },
  {
    "path": "text_db_llm/data/health.txt",
    "content": "    \r\n    which requires that you sit at a desk all day.  ; If you hate to talk\r\n     politics, don't associate with people who love to talk politics, etc.   Learn to live one day at a time.   Every day, do something you really enjoy.   Add an ounce of love to everything you do.   Take a hot bath or shower (or a cool one in summertime) to relieve tension.   Do something for somebody else.   Focus on understanding rather than on being understood; on loving rather than on being loved.   Do something that will improve your appearance.  ; Looking better can help you feel better.   Schedule a realistic day.  ; Avoid the tendency to schedule back-to-back appointments; allow time between appointments for a breathing spell.   Become more flexible.  ; Some things are worth not doing perfectly and\r\n some issues are fine to compromise upon.   Eliminate destructive self-talking\r\n\r\n I also felt they protected me from the hard road by interposing a layer of air between the sole of my foot and the pavement.   So why was I sidelined with a heel injury for over two   s?  I listened to the manufacturer and changed my runners every 400 miles.  Come to think of it, why do I see so many runners with lower extremity injuries in my office?  The traditional answer to these questions has always been overuse often compounded by an underlying mechanical abnormality such as over-pronation or flat-feet.  The treatment, along with modification of training, physiotherapy, stretching etc. has always included a close look at the runner's footwear, often with recommendations about motion control, stability, cushioning, orthotics or custom molded insoles.\r\n I do not recommend that you run your next half-marathon barefoot.  But certainly, I predict that sooner or later, changes will come about in both shoe design and training.  From the medical establishment's point of view, the prevention and treatment of running injuries must change to incorporate the concepts outlined above.  In fact I view the ideas I've presented here as a major paradigm shift in sports medicine, the likes of which I have not seen in the last fifteen years.  Of course, the major shoe companies have to own up and start introducing better shoes into their lines.\r\n\r\n\t\tadaptive immunity: The ability of the body to learn to fight specific infections after being exposed to the germs that cause them.\r\n\r\n\t\taddiction: Loss of control over indulging in a substance or performing an action or behavior, and continued craving for it despite negative consequences.\r\n\r\n\t\t\r\n\t\tadenosine triphosphate: An energy-storing molecule that is found in all human cells. Usually abbreviated as ATP.\r\n\r\n\t\tadequate intake: An    of the amount of a nutrient needed by healthy people. The Adequate Intake is used when there isn’t enough information to set a recommended dietary allowance (RDA).\r\n\r\n\t\tagoraphobia: Fear and avoidance of public places and open spaces.\r\n\r\n\t\tamnesia: Unusual memory loss or forgetfulness.\r\n\r\n\t\tamputation: The surgical removal of a limb or other body part.\r\n\r\n\t\tanaerobic: Any process that doesn’t require oxygen. Often refers to a form of short, high intensity exercise, known as anaerobic exercise.\r\n\r\n\t\tanaerobic exercise: Exercise that improves the efficiency of energy-producing systems that do not rely on oxygen. Examples include sprinting and weight lifting.\r\n\r\n\t\t\r\n\t\tdry eye: Stinging, burning, or irritation that occurs when the eye doesn’t produce enough moisture.\r\n\r\n\t\tduct: A tube or vessel in the body which carries the secretion of a gland; Secretion examples are tears, breast milk, etc.\r\n\r\n\r\n\t\tupper airway resistance syndrome: Inhalation that requires undue extra exertion; this extra work may cause insomnia and daytime sleepiness.\r\n\r\n\t\turea: A waste product of protein digestion and metabolism.\r\n\r\n\t\tureter: The tube that connects each kidney to the bladder.\r\n\r\n\t\turethra: The tube leading from the bladder through which urine is carried from the body.\r\n"
  },
  {
    "path": "text_db_llm/data/sports.txt",
    "content": "Sport is generally recognised as activities based in physical athleticism or physical dexterity.[3] Sports are usually governed by rules to ensure fair competition and consistent adjudication of the winner.\n\n\"Sport\" comes from the Old French desport meaning \"leisure\", with the oldest definition in English from around 1300 being \"anything humans find amusing or entertaining\".[4]\n\nOther bodies advocate widening the definition of sport to include all physical activity and exercise. For instance, the Council of Europe include all forms of physical exercise, including those completed just for fun.\n\n"
  },
  {
    "path": "text_db_llm/requirements.txt",
    "content": "openai==1.42.0\nchromadb==0.5.5\nllama-index-vector-stores-chroma==0.2.0\nllama-index-embeddings-huggingface==0.3.1\nllama-index==0.11.1"
  },
  {
    "path": "text_db_llm/text_db_llm.py",
    "content": "# Text Database Supporting Search and Chat-based Exploration\n\n# make sure you set the following environment variable:\n#   OPENAI_API_KEY\n\nimport os\nfrom llama_index.core import VectorStoreIndex, SimpleDirectoryReader\nimport chromadb\nfrom llama_index.vector_stores.chroma import ChromaVectorStore\nfrom llama_index.core import StorageContext\nfrom llama_index.embeddings.huggingface import HuggingFaceEmbedding\n\nchroma_client = chromadb.EphemeralClient()\nchroma_collection = chroma_client.create_collection(\"temp\")\n\nembed_model = HuggingFaceEmbedding(model_name=\"BAAI/bge-base-en-v1.5\")\n\nvector_store = ChromaVectorStore(chroma_collection=chroma_collection)\nstorage_context = StorageContext.from_defaults(vector_store=vector_store)\n\ndocuments = SimpleDirectoryReader('data').load_data()\nindex = VectorStoreIndex.from_documents(\n    documents, storage_context=storage_context, embed_model=embed_model\n)\nquery_engine = index.as_query_engine()\nprint(query_engine.query(\"effect of body chemistry on exercise?\"))\n\nexit(0)\n\nif os.path.exists(\"index.json\") and os.path.getsize(\"index.json\") > 0:\n  print(\"Loading index from disk...\")\n  index = VectorStoreIndex.load_from_disk('index.json')\nelse:\n  print(\"Index file odes not exist, so create it...\")\n  documents = SimpleDirectoryReader('data').load_data()\n  index = VectorStoreIndex(documents)\n  print(dir(index))\n  index.save_to_disk('index.json')\n\n# search for a document\n\n\nprint(index.query(\"effect of body chemistry on exercise?\"))\n"
  },
  {
    "path": "tool_search_math_example/requirements.txt",
    "content": "langchain\nduckduckgo_search\nlangchain-community\nlangchain-openai"
  },
  {
    "path": "tool_search_math_example/tool_search_math_example.py",
    "content": "from langchain.agents import create_react_agent, Tool, AgentExecutor\nfrom langchain.agents.agent_types import AgentType\nfrom langchain_community.tools import DuckDuckGoSearchRun\nfrom langchain_openai import ChatOpenAI\nfrom langchain_core.prompts import PromptTemplate\nimport os\n\nopenai_key = os.getenv(\"OPENAI_API_KEY\")\nllm = ChatOpenAI(model=\"gpt-4o-mini\", temperature=0.3, openai_api_key=openai_key)\n\nddg_api = DuckDuckGoSearchRun()\n\nclass DuckDuckGoSearchAPIWrapper:\n    def __call__(self, query):\n        results = ddg_api.invoke(query)\n        #print(f\"**** {results=}\")\n        return results if results else 'No results found'\n\nclass SimpleCalculator:\n    def __call__(self, expression):\n        try:\n            return eval(expression)\n        except Exception as e:\n            return f\"Error in calculation: {e}\"\n\n# Initialize the tools\nsearch_tool = Tool(\n    name=\"duckduckgo_search\",\n    func=DuckDuckGoSearchAPIWrapper(),\n    description=\"Searches the web using DuckDuckGo\"\n)\n\ncalculator_tool = Tool(\n    name=\"simple_calculator\",\n    func=SimpleCalculator(),\n    description=\"Performs simple calculations\"\n)\n\n# Define the tool chain\ntools = [search_tool, calculator_tool]\ntool_names = [\"duckduckgo_search\", \"simple_calculator\"]\n\nagent_scratchpad = \"thoughts: \"\n\ntemplate = '''Answer the following questions as best you can. You have access to the following tools:\n\n{tools}\n\nUse the following format:\n\nQuestion: the input question you must answer\nThought: you should always think about what to do\nAction: the action to take, should be one of [{tool_names}]\nAction Input: the input to the action\nObservation: the result of the action\n... (this Thought/Action/Action Input/Observation can repeat N times)\nThought: I now know the final answer\nFinal Answer: the final answer to the original input question\n\nBegin!\n\nQuestion: {input}\nThought:{agent_scratchpad}'''\n\nprompt = PromptTemplate.from_template(template)\nprint(prompt)\n\n# Initialize the agent with tools\nagent = create_react_agent(llm, tools, prompt)\n\nagent_executor = AgentExecutor(agent=agent, tools=tools)\n\n# Example #1 input for the chain\ninput_text = \"search: What is the population of Canada?\"\n\n# Run the chain\nresult = agent_executor.invoke({\"input\": input_text, \"chat_history\": agent_scratchpad})\n\n# Print the result\nprint(result)\n\n# Example #2 input for the chain\ninput_text = \"calculator: 250 * 4\"\n\n# Run the chain\nresult = agent_executor.invoke({\"input\": input_text, \"chat_history\": agent_scratchpad})\n\n# Print the result\nprint(result)"
  },
  {
    "path": "tools_langchain/doc_example.py",
    "content": "# example from documentation: https://github.com/cristobalcl/LearningLangChain/blob/master/notebooks/01%20-%20Simple%20Agent.ipynb\n\nfrom langchain.agents import initialize_agent, Tool\nfrom langchain.llms import OpenAI\n\nllm = OpenAI(temperature=0)\n\n# define 2 tools to experiment with\n\ndef add(input: str) -> str:\n    values = [int(x) for x in input.split(\"+\")]\n    return str(sum(values))\n\ndef is_prime(input: str) -> str:\n    n = int(input)\n\n    if n <= 1:\n        return \"no\"\n    if n <= 3:\n        return \"yes\"\n    if n % 2 == 0 or n % 3 == 0:\n        return \"no\"\n\n    i = 5\n    while i * i <= n:\n        if n % i == 0 or n % (i + 2) == 0:\n            return \"no\"\n        i += 6\n\n    return \"yes\"\n\ntools = [\n    Tool(\n        name = \"Add\",\n        func=add,\n        description=\"Useful for when you need to add numbers. Input should be in the form '1 + 2 + 3'.\"\n    ),\n    Tool(\n        name = \"IsPrime\",\n        func=is_prime,\n        description=\"Useful to know if a number is prime.\"\n    ),\n]\n\n# initialize the agent\n\nagent = initialize_agent(tools, llm, agent=\"zero-shot-react-description\", verbose=True)\n\ndef test1():\n    print(agent.run(\"If we add 3, 5, 19 20 and 24, is the result a prime number?\"))\n\ndef test2():\n    print(agent.run(\"Loop over the numbers from 10 to 30 and sum up the prime numbers\"))\n    \ntest2()\n    \n"
  },
  {
    "path": "tools_langchain/loop_collection_with_function.py",
    "content": "# example from documentation: https://github.com/cristobalcl/LearningLangChain/blob/master/notebooks/01%20-%20Simple%20Agent.ipynb\n\nfrom langchain.agents import initialize_agent, Tool\nfrom langchain_community.llms import OpenAI\n\nfrom typing import Callable\n\nllm = OpenAI(temperature=0)\n\n# define a looping tool\n\n\ndef loop(a_function: Callable, a_collection: list) -> list: # needs Python 3.9 or above\n    print(f\"Function: {a_function} Collection: {a_collection}\")\n    result = []\n    for item in a_collection:\n        result.append(a_function(item))\n    return result \n\ndef loop2(input: str) -> str:\n    values = input.split(\"to\")\n    start = int(values[0])\n    end = int(values[1])\n    result = 0\n    for i in range(start, end + 1):\n        result += i\n    return str(result)  \n\n\n# define 2 tools to experiment with\n\ndef add(input: str) -> str:\n    values = [int(x) for x in input.split(\"+\")]\n    return str(sum(values))\n\ndef is_prime(input: str) -> str:\n    n = int(input)\n\n    if n <= 1:\n        return \"no\"\n    if n <= 3:\n        return \"yes\"\n    if n % 2 == 0 or n % 3 == 0:\n        return \"no\"\n\n    i = 5\n    while i * i <= n:\n        if n % i == 0 or n % (i + 2) == 0:\n            return \"no\"\n        i += 6\n\n    return \"yes\"\n\ntools = [\n    Tool(\n        name = \"Loop\",\n        func=loop,\n        description=\"Applies a boolean function to each element of a collection and if the function returns true then add the element to the result. Input should be in the form 'function collection'.\"\n    ),\n    Tool(\n        name = \"Add\",\n        func=add,\n        description=\"Useful for when you need to add numbers. Input should be in the form '1 + 2 + 3'.\"\n    ),\n    Tool(\n        name = \"IsPrime\",\n        func=is_prime,\n        description=\"Useful to know if a number is prime.\"\n    ),\n]\n\n# initialize the agent\n\nagent = initialize_agent(tools, llm, agent=\"zero-shot-react-description\", verbose=True)\n\ndef test1():\n    print(agent.run(\"If we add 3, 5, 19 20 and 24, is the result a prime number?\"))\n\ndef test2():\n    print(agent.run(\"Loop over the collection [10, 11, 12, 13, 14] and test each for being a prime number. Sum up the prime numbers\"))\n\ndef test3():\n    def foo(x):\n        return x + 1\n    print(loop(foo, [1, 2, 3, 4, 5]))\n\ndef test4():\n    print(loop(\"1to10\"))\n\ntest2()\n    \n"
  }
]