import os,sys
from collections import  Counter
from typing import Optional
import json

from sklearn.model_selection import train_test_split

import numpy as np
from pandas import DataFrame
import pandas as pd

from sentiment.exception import SentimentException
from sentiment.logger import logging
from sentiment.entity.config_entity import DataTransformationConfig
from sentiment.entity.artifact_entity import DataTransformationArtifact, DataValidationArtifact
from sentiment.utils.main_utils import read_yaml_file, save_numpy_array_data
from sentiment.constant.training_pipeline import SCHEMA_FILE_PATH, STOPWORD, CORPUS,\
            S3_ARTIFACTS_URI, ENCODING, VOCAB_FILE_PATH
from sentiment.configuration.s3_operations import S3Operation
from sentiment.cloud_storage.s3_syncer import S3Sync

#nltk
import nltk
from nltk.corpus import stopwords
nltk.download('stopwords')
from nltk.util import ngrams
stop=set(stopwords.words(STOPWORD))
nltk.download(CORPUS)

class DataTransformation:

    def __init__(self, data_transformation_config: DataTransformationConfig, data_validation_artifacts: DataValidationArtifact) -> None:
        try:
            self.data_transformation_config=data_transformation_config
            self.data_validation_artifacts = data_validation_artifacts
            self._schema_config = read_yaml_file(SCHEMA_FILE_PATH)
        except Exception as e:
            raise SentimentException(e,sys)

    def change_target_values(self, raw_data_path: str) -> DataFrame:
        """Change the initial target values of 4 to 2

        Returns:
            DataFrame: transformed dataframe
        """
        t = []
        raw_data = pd.read_csv(raw_data_path, encoding=ENCODING)
        for i in raw_data.target.values:
            if i ==0: t.append(0)
            else: t.append(1)

        selected_df = pd.DataFrame({"text":raw_data.text.values, "target":t})
        return selected_df

    def _delete_unwanted_symbols(self) -> dict:
        """Return the dictionary having ord() of all the symbols

        Returns:
            dict: _description_
        """
        remove_dict = {ord(c):f'' for c in self.data_transformation_config.symbols_to_delete}
        return remove_dict

    def _handle_punctuation(self, x):
        remove_dict = self._delete_unwanted_symbols()
        x = x.translate(remove_dict)
        return x

    def preprocess_string(self, x: str):
        """
        Applying all the preprocessing steps defined above.

        """
        try:
            x = x.lower()
            x = self._handle_punctuation(x)
            return x
        except Exception as e:
            raise SentimentException(e, sys)

    def _remove_stopwords(self, df: Optional[np.array]) -> list:
        """Returns all the words in whole dataset after transformation
        """
        try:
            word_list = []
            stop_words = set(stopwords.words('english'))
            for sent in df:
                for word in sent.lower().split():
                    word = self.preprocess_string(word)
                    if word not in stop_words and word != '':
                        word_list.append(word)
            return word_list
        except Exception as e:
            raise SentimentException(e, sys)

    def split_dataset(self, df: DataFrame):
        try:
            logging.info("Starting Splitting of dataset>>>>")
            X,y = df['text'].values, df['target'].values
            x_train,x_test,y_train,y_test = train_test_split(X,y,stratify=y, test_size=self.data_transformation_config.test_size)
            logging.info(f'shape of train data is {x_train.shape}')
            logging.info(f'shape of train data is {x_test.shape}')
            logging.info(">>>>>>> Splitting of dataset Completed")
            return x_train,x_test,y_train,y_test
        except Exception as e:
            raise SentimentException(e, sys)

    def tockenize(self, x_train, y_train, x_test, y_test, word_list: list):
        """Tokenize and Vectorize using One hot encoding
        """
        try:
            logging.info("Starting Tokenizationm>>>>>>")
            corpus = Counter(word_list)
            # sorting on the basis of most common words
            corpus_ = sorted(corpus,key=corpus.get,reverse=True)[:1000]
            # creating a dict
            onehot_dict = {w:i+1 for i,w in enumerate(corpus_)}

            # tockenize
            final_list_train,final_list_test = [],[]
            for sent in x_train:
                    final_list_train.append([onehot_dict[self.preprocess_string(word)] for word in sent.lower().split()
                                            if self.preprocess_string(word) in onehot_dict.keys()])
            for sent in x_test:
                    final_list_test.append([onehot_dict[self.preprocess_string(word)] for word in sent.lower().split()
                                            if self.preprocess_string(word) in onehot_dict.keys()])

            return np.array(final_list_train), np.array(y_train), np.array(final_list_test), np.array(y_test), onehot_dict
        except Exception as e:
            raise SentimentException(e, sys)

    def padding_(self, sentences, seq_len):
        try:
            features = np.zeros((len(sentences), seq_len),dtype=int)
            for ii, review in enumerate(sentences):
                if len(review) != 0:
                    features[ii, -len(review):] = np.array(review)[:seq_len]
            return features
        except Exception as e:
            raise SentimentException(e, sys)

    def initiate_data_transformation(self) -> DataTransformationArtifact:
        try:
            selected_df = self.change_target_values(self.data_validation_artifacts.valid_file_path)
            x_train,x_test,y_train,y_test = self.split_dataset(selected_df[:10000]) # 1600000
            all_words = self._remove_stopwords(x_train)
            x_train,y_train,x_test,y_test,vocab = self.tockenize(x_train,y_train, x_test,y_test, all_words)
            x_train_pad = self.padding_(x_train,self.data_transformation_config.seq_len)
            x_test_pad = self.padding_(x_test,self.data_transformation_config.seq_len)
            logging.info("Saving Transformed data to artifacts>>>")

            os.makedirs(self.data_transformation_config.train_data_dir, exist_ok= True)
            os.makedirs(self.data_transformation_config.test_data_dir, exist_ok= True)

            save_numpy_array_data(self.data_transformation_config.train_data_file_path, x_train_pad)
            save_numpy_array_data(self.data_transformation_config.train_labels_file_path, y_train)
            save_numpy_array_data(self.data_transformation_config.test_data_file_path, x_test_pad)
            save_numpy_array_data(self.data_transformation_config.test_labels_file_path, y_test)
            data_transformation_artifact = DataTransformationArtifact(
                            train_data_file_path = self.data_transformation_config.train_data_file_path,
                            train_labels_file_path = self.data_transformation_config.train_labels_file_path,
                            test_data_file_path = self.data_transformation_config.test_data_file_path,
                            test_labels_file_path =  self.data_transformation_config.test_labels_file_path,
                            vocab = vocab)
            transformation_artifacts_dir = self.data_transformation_config.data_transformation_artifacts_dir

            # Save vocab for prediction
            with open(VOCAB_FILE_PATH, 'w') as fp:
                json.dump(vocab, fp)

            s3_sync = S3Sync()
            logging.info("Sync transformation files to S3 transformation artifacts folder...")
            s3_sync.sync_folder_to_s3(folder=transformation_artifacts_dir, aws_bucket_url=S3_ARTIFACTS_URI)
            logging.info("Finished Syncing files to S3 transformation artifacts folder")
            return data_transformation_artifact
        except Exception as e:
            raise SentimentException(e, sys)


import os
import time
import argparse

parser = argparse.ArgumentParser()
# ENVIRONMENT SETTINGS:
parser.add_argument('--save_dir', type=str, default=os.environ['SCRATCH'])
parser.add_argument('--bs', type=int, default=100)
#
args = parser.parse_args()
print (args)
args_dict = args.__dict__
args_str = " --".join([""] + [k + "=" + str(v) for k,v in args_dict.items()]) + " "
print (args_str)
locals().update(args_dict)

bash_script_name= os.path.join(os.environ['HOME'], 'examples/mnist', 'niagara_launch_helper.sh ')
python_script_name= os.path.join(os.environ['HOME'], 'examples/mnist', 'STML.py ')

def wwrite(f_, ss):
    f_.write(ss + '\n')

with open('niagara_launcher.sh', 'a') as f_:
    f_.write(
    '#!/bin/bash')
    f_.write('\n')
    f_.write(
    '#SBATCH --nodes=1')
    f_.write('\n')
    f_.write(
    '#SBATCH --ntasks-per-node=40')
    f_.write('\n')
    f_.write(
    '#SBATCH --time=48:00:00')
    f_.write('\n')
    f_.write(
    '#SBATCH --job-name serialx40')
    f_.write('\n')
    f_.write(
    '#SBATCH --account=def-bengioy')
    f_.write('\n')
    f_.write(
    'source activate base')
    f_.write('\n')
    f_.write(
    'export OMP_NUM_THREADS=1')

    # START HERE
    args_str = ' --batch-size=' + str(bs)
    for starting_seed in range(5):
        for shuffle in [True, False]:
            for thresh in [-np.inf, -.001, 0, .001]:#,10]:
                ss = ' --starting_seed=' + str(starting_seed) + ' --environment_swapping=' + str(environment_swapping) + ' --PBT_interval=' + str(PBT_interval)
                launch_str = " ".join([bash_script_name, python_script_name, args_str, ss])
                f_.write('\n')
                f_.write('(' + launch_str + ' && echo "job 01 finished") &')
    f_.write('\n')
    f_.write('wait')

os.system('chmod 755 niagara_launcher.sh')
#os.system('./niagara_launcher.sh')


"""Primary class for converting fiber photometry data (dLight fluorescence)."""
# Standard Scientific Python
import numpy as np
import joblib

# NWB Ecosystem
from pynwb.file import NWBFile
from pynwb.ophys import RoiResponseSeries
from ..markowitz_gillis_nature_2023.rawfiberphotometryinterface import RawFiberPhotometryInterface
from neuroconv.tools import nwb_helpers
from hdmf.backends.hdf5.h5_utils import H5DataIO

class FiberPhotometryInterface(RawFiberPhotometryInterface):
    def __init__(
        self,
        file_path: str,
        tdt_path: str,
        tdt_metadata_path: str,
        session_uuid: str,
        session_id: str,
        session_metadata_path: str,
        subject_metadata_path: str,
    ):
        super().__init__(
            file_path=file_path,
            tdt_path=tdt_path,
            tdt_metadata_path=tdt_metadata_path,
            session_uuid=session_uuid,
            session_id=session_id,
            session_metadata_path=session_metadata_path,
            subject_metadata_path=subject_metadata_path,
        )

    def add_to_nwbfile(self, nwbfile: NWBFile, metadata: dict) -> None:
        SAMPLING_RATE = 30
        super().add_to_nwbfile(nwbfile, metadata)
        processed_photometry = joblib.load(self.source_data["file_path"])
        timestamps = np.arange(processed_photometry["dlight"].shape[0]) / SAMPLING_RATE
        signal_series = RoiResponseSeries(
            name="SignalF",
            description=(
                "Demodulated raw fluorescence (F) from the blue light excitation (470nm) "
                "(See Methods: Photometry Active Referencing)."
            ),
            data=H5DataIO(processed_photometry["dlight"], compression=True),
            unit="n.a.",
            timestamps=H5DataIO(timestamps, compression=True),
            rois=self.fibers_ref,
        )
        reference_series = RoiResponseSeries(
            name="UVReferenceF",
            description=(
                "Demodulated raw fluorescence (F) from the isosbestic UV excitation (405nm) "
                "(See Methods: Photometry Active Referencing)."
            ),
            data=H5DataIO(processed_photometry["uv"], compression=True),
            unit="n.a.",
            timestamps=signal_series.timestamps,
            rois=self.fibers_ref,
        )
        ophys_module = nwb_helpers.get_module(nwbfile, name="ophys", description="Fiber photometry data")
        ophys_module.add(signal_series)
        ophys_module.add(reference_series)


from __future__ import absolute_import, division, print_function

__metaclass__ = type

from ansible_collections.netbox.netbox.plugins.module_utils.netbox_utils import (
    NetboxModule,
    ENDPOINT_NAME_MAPPING,
    SLUG_REQUIRED,
)

NB_CONFIG_CONTEXTS = "config_contexts"
NB_TAGS = "tags"
NB_CUSTOM_FIELDS = "custom_fields"
NB_CUSTOM_LINKS = "custom_links"
NB_EXPORT_TEMPLATES = "export_templates"
NB_JOURNAL_ENTRIES = "journal_entries"
NB_WEBHOOKS = "webhooks"
NB_CONFIG_TEMPLATES = "config_templates"

class NetboxExtrasModule(NetboxModule):
    def __init__(self, module, endpoint):
        super().__init__(module, endpoint)

    def _handle_state_new(self, nb_app, nb_endpoint, endpoint_name, data):
        if self.state == "new":
            self.nb_object, diff = self._create_netbox_object(nb_endpoint, data)
            self.result["msg"] = "%s created" % (endpoint_name)
            self.result["changed"] = True
            self.result["diff"] = diff

    def run(self):
        """
        This function should have all necessary code for endpoints within the application
        to create/update/delete the endpoint objects
        Supported endpoints:
        - config_contexts
        - config_templates
        - tags
        - journal entries
        """
        # Used to dynamically set key when returning results
        endpoint_name = ENDPOINT_NAME_MAPPING[self.endpoint]

        self.result = {"changed": False}

        application = self._find_app(self.endpoint)
        nb_app = getattr(self.nb, application)
        nb_endpoint = getattr(nb_app, self.endpoint)
        user_query_params = self.module.params.get("query_params")

        data = self.data

        # Used for msg output
        if data.get("name"):
            name = data["name"]
        elif data.get("slug"):
            name = data["slug"]

        if self.endpoint in SLUG_REQUIRED:
            if not data.get("slug"):
                data["slug"] = self._to_slug(name)

        # Make color params lowercase
        if data.get("color"):
            data["color"] = data["color"].lower()

        # Handle journal entry
        if self.state == "new" and endpoint_name == "journal_entry":
            self._handle_state_new(nb_app, nb_endpoint, endpoint_name, data)
        else:
            object_query_params = self._build_query_params(
                endpoint_name, data, user_query_params
            )
            self.nb_object = self._nb_endpoint_get(
                nb_endpoint, object_query_params, name
            )

        if self.state == "present":
            self._ensure_object_exists(nb_endpoint, endpoint_name, name, data)
        elif self.state == "absent":
            self._ensure_object_absent(endpoint_name, name)

        try:
            serialized_object = self.nb_object.serialize()
        except AttributeError:
            serialized_object = self.nb_object

        self.result.update({endpoint_name: serialized_object})

        self.module.exit_json(**self.result)


#!/home/apb/anaconda3/bin/python

import os
from tinydb import TinyDB, Query
import pprint
from collections import namedtuple
from ask import Ask

pp = pprint.PrettyPrinter(indent=4)

class Results:
    """Extract and Process Results"""

    def __init__(self):
        """Setup"""
        self.db = TinyDB("../data/runs/NoPLearning_data.json")
        self.record = Query()
        self.ask = Ask()

    def overview(self):
        for run in self.db:
            results = run["training_data"]['test'][-1]
            pp.pprint({"id":run.doc_id, "start":run["start_time"], "result":results})

    def select(self):
        self.overview()
        while True:
            n = int(self.ask("What input do you want to see", "select", 0))
            if self.db.contains(doc_id=n):
                break
            else:
                print("unknown id")
        return n, self.db.get(doc_id=n)

    def show(self):
        n, result = self.select()
        print("Showing results for id {:d}".format(n))
        pp.pprint(result)

    def delete(self):
        n, result = self.select()
        pp.pprint(result)
        ans = input("Are you sure you want to delete record [N/y]? ")
        if ans == "y":
            os.remove(result['args']["weightFile"])
            self.db.delete(doc_id=n)

    def garbage_collection(self):
        for root, dirs, files in os.walk("../data/runs/weights"):
            print(files)
            for f in files:
                if not self.db.contains(self.record.args.weightFile == f):
                    print("removing", f)
                    os.remove(root+"/"+f)
                else:
                    print("keeping", f)

    def selectAction(self):
        actions = {"overview": self.overview,
                   "show": self.show,
                   "delete": self.delete,
                   "garbage collection": self.garbage_collection,
                   "quit": exit
                   }
        while True:
            actionList = []
            for i, action in enumerate(actions.keys()):
                actionList.append(action)
                print(i, action)
            n = int(self.ask("What action do you want to take", "action", 0))
            if n>=0 and n<len(actionList):
                break
        actions[actionList[n]]()

    def loop(self):
        while True:
            self.selectAction()

if __name__ == "__main__":
    Results().loop()


import cv2
import torch
import numpy as np

class ColorHistograms(object):
    def __call__(self, img):
        img = np.array(img)
        img_bgr = img[:, :, ::-1].copy()

        t_img = torch.Tensor(8, 256, 1)

        for i in range(0, 3):
            t_img[i] = torch.Tensor(cv2.calcHist([img_bgr], [i], None, [256], [0, 256]))

        img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

        for i in range(0, 3):
            t_img[i + 3] = torch.Tensor(cv2.calcHist([img_hsv], [i], None, [256], [0, 256]))

        img_yuv = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)

        for i in range(1, 3):
            t_img[i + 5] = torch.Tensor(cv2.calcHist([img_yuv], [i], None, [256], [0, 256]))

        return t_img.cuda()


import socket
import struct
import sys

from multiprocessing import Process

import kilgrave_collector
from config import *

def send_orders(order):
  for machine in target_machines:
    executor = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
      executor.connect((machine, executor_port))
      executor.sendall(order)
      response = executor.recv(len(execution_ack_string))
      print (response)
      executor.close()
    except socket.error:
      print("Could not connect to {}.".format(machine))

def build_order():
  raw_order = ' '.join(sys.argv[1:])
  kilgrave_order = length_padder(len(raw_order)) + raw_order
  print(kilgrave_order)
  return kilgrave_order

if __name__ == "__main__":
  try:
    if (len(sys.argv) == 1):
      print("You can't run kilgrave without args")
      exit(1)
    collector = Process(target=kilgrave_collector.main)
    collector.start()
    order = build_order()
    send_orders(order)
    collector.join()
  except KeyboardInterrupt:
    print("Keyboard Interrupt Caught")


#!/usr/bin/python
import inspect
import ldtp
from ldtpd.core import Ldtpd

#This program is to extract the api out of the loaded ldtp library using introspection.
#This is mostly helpful whenever ldtp updates or a new version comes out.
#This section works on ldtp v3

def defaultCount(spec):
  x = spec[3]
  if (x):
    return len(x)
  else:
    return 0

def checkValid(symbol):
    if symbol.startswith('_'):
        return False
    obj = getattr(Ldtpd, symbol)
    if not callable(obj):
        return False
    return True

names = filter(lambda fn: checkValid(fn), dir(Ldtpd))
specs = map(lambda n: inspect.getargspec(getattr(Ldtpd, n)), names)
map(lambda s: s[0].remove('self'), specs)
argz  = map(lambda s: [ s[0], defaultCount(s)], specs)
api = zip(names, argz)
print(str(api).replace("'", '"'))


import torch
import torchvision
from torch import nn
from diffusers import DDPMScheduler, DDIMScheduler, UNet2DModel
from matplotlib import pyplot as plt
from tqdm import tqdm
from torch.utils.data import DataLoader

# define class-conditioned u-net model from huggingface
class ClassConditionedUNet(nn.module):
    def __init__(self, num_classes, class_emb_size, sample_size, c_in, c_out):
        super().__init__()
        self.class_emb = nn.Embedding(num_classes, class_emb_size)

        self.model = UNet2DModel(
            sample_size=sample_size,
            in_channels=c_in + class_emb_size,
            out_channels=c_out,
            layers_per_block=2,
            block_out_channels=(32, 64, 64),
            down_block_types=(
                "DownBlock2D",
                "AttnDownBlock2D",
                "AttnDownBlock2D",
            ),
            up_block_types=(
                "AttnUpBlock2D",
                "AttnUpBlock2D",
                "UpBlock2D",
            ),
        )

    def forward(self, x, t, class_labels):
        bs, ch, w, h = x.shape
        class_cond = self.class_emb(class_labels)
        class_cond = class_cond.view(bs, class_cond.shape[1], 1, 1).expand(bs, class_cond.shape[1], w, h)
        net_input = torch.cat((x, class_cond), 1)
        return self.model(net_input, t).sample

# define diffusion class from huggingface
class Diffuser():
  def __init__(self, device, dataloader, scheduler='ddpm', num_train_timesteps=1000, beta_schedule='squaredcos_cap_v2', num_classes=10, class_emb_size=4, sample_size=28, c_in=1, c_out=1):
    self.num_classes = num_classes
    self.train_dataloader = dataloader
    self.num_train_timesteps = num_train_timesteps
    self.net = ClassConditionedUNet(num_classes=num_classes, class_emb_size=class_emb_size, sample_size=sample_size, c_in=c_in, c_out=c_out).to(device)
    self.sample_size = sample_size
    self.channels = c_in
    self.device = device

    if scheduler=='ddim':
        self.noise_scheduler = DDIMScheduler(num_train_timesteps=self.num_train_timesteps, beta_schedule=beta_schedule)
        self.noise_scheduler.set_timesteps(self.num_train_timesteps)
    else:
        self.noise_scheduler = DDPMScheduler(num_train_timesteps=self.num_train_timesteps, beta_schedule=beta_schedule)

  def train(self, n_epochs=5, lr=1e-3):
    self.loss_fn = nn.MSELoss()
    self.opt = torch.optim.Adam(self.net.parameters(), lr)
    self.losses = []

    for epoch in range(n_epochs):
      for x,y in tqdm(self.train_dataloader):
        x = x.to(self.device)
        y = y.to(self.device)
        noise = torch.randn_like(x)
        timesteps = torch.randint(0, self.num_train_timesteps-1, (x.shape[0],)).long().to(self.device)
        noisy_x = self.noise_scheduler.add_noise(x,noise,timesteps)

        pred = self.net(noisy_x, timesteps, y)
        loss = self.loss_fn(pred, noise)

        self.opt.zero_grad()
        loss.backward()
        self.opt.step()

        self.losses.append(loss.item())

  def sample(self):
    x = torch.randn(80, self.channels, self.sample_size, self.sample_size).to(self.device)
    y = torch.tensor([[i]*8 for i in range(self.num_classes)]).flatten().to(self.device)

    for i,t in tqdm(enumerate(self.noise_scheduler.timesteps)):
      with torch.no_grad():
        residual = self.net(x,t,y)

      x = self.noise_scheduler.step(residual, t, x).prev_sample

    fig, ax = plt.subplots(1, 1, figsize=(12, 12))
    ax.imshow(torchvision.utils.make_grid(x.detach().cpu().clip(-1, 1), nrow=8)[0], cmap='Greys')
    return x

if __name__ == "__main__":
  device = 'mps' if torch.backends.mps.is_available() else 'cuda' if torch.cuda.is_available() else 'cpu'

  # import dataset
  mnist = torchvision.datasets.MNIST(root="mnist/", train=True, download=True, transform=torchvision.transforms.ToTensor())

  # create dataloaders
  train_dataloader = DataLoader(mnist, batch_size=128, shuffle=True)

  # generate images
  diffuser = Diffuser(dataloader=train_dataloader,
                    num_train_timesteps=1000,
                    beta_schedule='squaredcos_cap_v2',
                    num_classes=10,
                    class_emb_size=4,
                    sample_size=28,
                    c_in=1,
                    c_out=1)
  diffuser.train(n_epochs=150, lr=1e-4)
  samples = diffuser.sample()

  # generate 5040 images
  tests = []
  for _ in range(63):
    samples = diffuser.sample()
    tests.append(samples)

  # save images
  torch.save(tests, 'tests_ddpm.pt')

  # add class to images
  annotated_tests = []
  for batch in tests:
     for i in range(len(batch)):
        cur_num = int(i/8)
        annotated_tests.append((batch[i].cpu(), cur_num))

  # save images + class
  torch.save(annotated_tests, 'annotated_tests_ddpm.pt')


import sys

class RBnode():
    def __init__(self, key):
        self.key = key
        self.parent = None
        self.left = None
        self.right = None
        self.color = 1

class RBtree():
    def __init__(self):
        self.TNULL = RBnode(0)
        self.TNULL.color = 0
        self.TNULL.left = None
        self.TNULL.right = None
        self.root = self.TNULL

    def __print_helper(self, node, indent, last):
        if node != self.TNULL:
            sys.stdout.write(indent)
            if last:
                sys.stdout.write("R----")
                indent += "     "
            else:
                sys.stdout.write("L----")
                indent += "|    "

            s_color = "RED" if node.color == 1 else "BLACK"
            print(str(node.key) + "(" + s_color + ")")
            self.__print_helper(node.left, indent, False)
            self.__print_helper(node.right, indent, True)

    def print_tree(self):
        self.__print_helper(self.root, "", True)

    def get_root(self):
        return self.root

    def left_rotate(self, x):
        y = x.right
        x.right = y.left
        if y.left != self.TNULL:
            y.left.parent = x
        y.parent = x.parent
        # If parent of x is null then x was the root and y will be the root
        if x.parent == None:
            self.root = y
        # Else If x was the left child of its parent, set y to be the left of the parent
        elif x == x.parent.left:
            x.parent.left = y
        # Else if x was the right child of its parent, set y to be the right child of the parent
        else:
            x.parent.right = y
        y.left = x
        x.parent = y

    def right_rotate(self, x):
        y = x.left
        x.left = y.right
        if y.right != self.TNULL:
            y.right.parent = x

        y.parent = x.parent
        if x.parent == None:
            self.root = y
        elif x == x.parent.right:
            x.parent.right = y
        else:
            x.parent.left = y
        y.right = x
        x.parent = y

    def fix_insert(self, node):
        # Repeats while color of parent to the node is red
        while node.parent.color == 1:
            # If the parent of the node is the right child
            if node.parent == node.parent.parent.right:
                uncle = node.parent.parent.left
                if uncle.color == 1:
                    uncle.color = 0
                    node.parent.color = 0
                    node.parent.parent.color = 1
                    node = node.parent.parent
                else:
                    if node == node.parent.left:
                        node = node.parent
                        self.right_rotate(node)
                    node.parent.color = 0
                    node.parent.parent.color = 1
                    self.left_rotate(node.parent.parent)
            else:
                uncle = node.parent.parent.right

                if uncle.color == 1:
                    uncle.color = 0
                    node.parent.color = 0
                    node.parent.parent.color = 1
                    node = node.parent.parent
                else:
                    if node == node.parent.right:
                        node = node.parent
                        self.left_rotate(node)
                    node.parent.color = 0
                    node.parent.parent.color = 1
                    self.right_rotate(node.parent.parent)
            if node == self.root:
                break
        self.root.color = 0

    def insert(self, key):
        node = RBnode(key)
        node.parent = None
        node.left = self.TNULL
        node.right = self.TNULL
        node.color = 1

        y = None
        x = self.root

        # Traverse the tree to find the position to insert the node
        while x != self.TNULL:
            # Y is a pointer to x
            y = x
            if node.key < x.key:
                x = x.left
            else:
                x = x.right

        # Y is a pointer to the parent of the inserted node
        node.parent = y
        # If the parent to the item is none then this item is the root of the tree
        if y is None:
            self.root = node
        # Put the inserted item to the left or right of its parent according to its value
        elif node.key < y.key:
            y.left = node
        else:
            y.right = node
        # If the node inserted is the root, then color it black and the insertion is done
        if node.parent is None:
            node.color = 0
            return

        if node.parent.parent is None:
            return
        # If the parent of the inserted node is red then we call fix_insert to fix the RB tree according to its
        # properties
        self.fix_insert(node)

    def height(self, node):
        if node == self.TNULL:
            return 0
        return 1 + max(self.height(node.left), self.height(node.right))

    def size(self, node):
        if node == self.TNULL:
            return 0
        return 1 + self.size(node.left) + self.size(node.right)

    def search(self, node, key):
        if node == self.TNULL or key == node.key:
            return node.key
        if key < node.key:
            return self.search(node.left, key)
        return self.search(node.right, key)

rbt = RBtree()
dictf = open('EN-US-Dictionary.txt', 'r')
dictionary = dictf.read().splitlines()
for word in dictionary:
    rbt.insert(word)
while True:
    choice = input('1- SIZE\n2- INSERT\n3- LOOK UP\n4- TREE HEIGHT\n5- QUIT\n\nEnter the number associated with your choice: ')
    if choice == '1':
        print(f'\nDictionary size: {rbt.size(rbt.get_root())}\n')
    elif choice == '2':
        word = input('\nEnter word you want to insert: ')
        found = rbt.search(rbt.get_root(), word) == word
        if found:
            print('\nERROR: Word already in the dictionary!\n')
        elif not found:
            rbt.insert(word)
            print(f'\nDictionary size: {rbt.size(rbt.get_root())}\n')
            print(f'Dictionary height: {rbt.height(rbt.get_root())}\n')
    elif choice == '3':
        word = input('\nEnter word you want to look up: ')
        found = rbt.search(rbt.get_root(), word) == word
        if found:
            print('\nYES\n')
        elif not found:
            print('\nNO\n')
    elif choice == '4':
        print(f'\nDictionary height: {rbt.height(rbt.get_root())}\n')
    elif choice == '5':
        break
    else:
        print('\nPlease enter a valid choice (1-5)\n')
dictf.close()


#--------------------------------
# Name:         build_static_files.py
# Purpose:      Build static files for ET-Demands from zonal stats ETCells
#--------------------------------

import argparse
from collections import defaultdict
import datetime as dt
import logging
import os
import re
import shutil
import sys

import _arcpy
import _util as util

def main(ini_path, area_threshold=10,
         beef_cuttings=4, dairy_cuttings=5,
         overwrite_flag=False):
    """Build static text files needed to run ET-Demands model

    Parameters
    ----------
    ini_path : str
        File path of the parameter INI file.
    area_threshold : float
        CDL area threshold [acres] (the default is 10 acres).
    beef_cuttings : int
        Initial number of beef hay cuttings (the default is 4).
    dairy_cuttings : int
        Initial number of dairy hay cuttings (the default is 5).
    overwrite_flag : bool
        If True, overwrite existing files (the default is False).

    Returns
    -------
    None

    """
    logging.info('\nBuilding ET-Demands Static Files')

    # Input units
    # Why isn't this read from .ini CROP_ET section?
    # station_elev_units = 'FEET'

    # Default values
    permeability = -999
    soil_depth = 60         # inches
    aridity = 50
    irrigation = 1
    # DEADBEEF - The number of crops should not be hardcoded here
    crops = 91

    # Input paths
    # DEADBEEF - For now, get cropET folder from INI file
    # This function may eventually be moved into the main cropET code
    crop_et_sec = 'CROP_ET'
    config = util.read_ini(ini_path, section=crop_et_sec)

    try:
        project_ws = config.get(crop_et_sec, 'project_folder')
    except:
        logging.error('project_folder parameter must be set in the INI file, '
                      'exiting')
        return False
    try:
        gis_ws = config.get(crop_et_sec, 'gis_folder')
    except:
        logging.error('gis_folder parameter must be set in the INI file, '
                      'exiting')
        return False
    try:
        et_cells_path = config.get(crop_et_sec, 'cells_path')
    except:
        logging.error('cells_path parameter must be set in the INI file, '
                      'exiting')
        return False
    try:
        stations_path = config.get(crop_et_sec, 'stations_path')
    except:
        logging.error('stations_path parameter must be set in the INI file, '
                      'exiting')
        return False
    try:
        crop_et_ws = config.get(crop_et_sec, 'crop_et_folder')
    except:
        logging.error('crop_et_folder parameter must be set in the INI file, '
                      'exiting')
        return False
    try:
        template_ws = config.get(crop_et_sec, 'template_folder')
    except:
        template_ws = os.path.join(os.path.dirname(crop_et_ws), 'static')
        logging.info(
            '\nStatic text file "template_folder" parameter was not set '
            'in the INI\n  Defaulting to: {}'.format(template_ws))

    # elevation units
    try:
        station_elev_units = config.get(crop_et_sec, 'elev_units')
    except:
        logging.error('elev_units must be set in crop_et section of INI file, '
                      'exiting')
        return False

    # Read data from geodatabase or shapefile
    # if '.gdb' in et_cells_path and not et_cells_path.endswith('.shp'):
    #     _flag = False
    #     _path = os.path.dirname(et_cells_path)
    #      gdb_path = r'D:\Projects\CAT_Basins\AltusOK\et-demands_py\et_demands.gdb'
    #     _cells_path = os.path.join(gdb_path, 'et_cells')

    # Output sub-folder names
    static_ws = os.path.join(project_ws, 'static')

    # Weather station shapefile fields
    station_id_field = 'STATION_ID'
    station_lat_field = 'LAT'
    station_lon_field = 'LON'
    if station_elev_units.upper() in ['FT', 'FEET']:
        station_elev_field = 'ELEV_FT'
    elif station_elev_units.upper() in ['M', 'METERS']:
        station_elev_field = 'ELEV_M'
    # station_elev_field = 'ELEV_FT'

    # ET Cell field names
    cell_lat_field = 'LAT'
    # cell_lon_field = 'LON'
    cell_id_field = 'CELL_ID'
    cell_name_field = 'CELL_NAME'
    # cell_station_id_field = 'STATION_ID'
    # awc_field = 'AWC'
    clay_field = 'CLAY'
    sand_field = 'SAND'
    awc_in_ft_field = 'AWC_IN_FT'
    hydgrp_num_field = 'HYDGRP_NUM'
    hydgrp_field = 'HYDGRP'

    # huc_field = 'HUC{}'.format(huc)
    # permeability_field = 'PERMEABILITY'
    # soil_depth_field = 'SOIL_DEPTH'
    # aridity_field = 'ARIDITY'
    # dairy_cutting_field = 'DAIRY_CUTTINGS'
    # beef_cutting_field = 'BEEF_CUTTINGS'

    # Static file names
    cell_props_name = 'ETCellsProperties.txt'
    cell_crops_name = 'ETCellsCrops.txt'
    cell_cuttings_name = 'MeanCuttings.txt'
    crop_params_name = 'CropParams.txt'
    crop_coefs_name = 'CropCoefs.txt'
    crop_coefs_eto = 'CropCoefs_eto.txt'
    crop_coefs_etr = 'CropCoefs_etr.txt'
    eto_ratio_name = 'EToRatiosMon.txt'
    etr_ratio_name = 'ETrRatiosMon.txt'
    static_list = [crop_params_name, crop_coefs_name, crop_coefs_eto,
                   crop_coefs_etr, cell_props_name, cell_crops_name,
                   cell_cuttings_name, eto_ratio_name, etr_ratio_name]

    # Check input folders
    if not os.path.isdir(crop_et_ws):
        logging.critical('\nERROR: The INI cropET folder does not exist'
                         '\n  {}'.format(crop_et_ws))
        sys.exit()
    elif not os.path.isdir(project_ws):
        logging.critical('\nERROR: The project folder does not exist'
                         '\n  {}'.format(project_ws))
        sys.exit()
    elif not os.path.isdir(gis_ws):
        logging.critical('\nERROR: The GIS folder does not exist'
                         '\n  {}'.format(gis_ws))
        sys.exit()
    logging.info('\nGIS Workspace:      {}'.format(gis_ws))
    logging.info('Project Workspace:  {}'.format(project_ws))
    logging.info('CropET Workspace:   {}'.format(crop_et_ws))
    logging.info('Template Workspace: {}'.format(template_ws))

    # Check input files
    if not _arcpy.exists(et_cells_path):
        logging.critical('\nERROR: The ET Cell shapefile does not exist'
                         '\n  {}'.format(et_cells_path))
        sys.exit()
    elif not _arcpy.exists(stations_path):
        logging.critical('\nERROR: The weather station shapefile does not exist'
                         '\n  {}'.format(stations_path))
        sys.exit()
    for static_name in static_list:
        if not os.path.isfile(os.path.join(template_ws, static_name)):
            logging.error(
                '\nERROR: The static template does not exist'
                '\n  {}'.format(os.path.join(template_ws, static_name)))
            sys.exit()
    logging.debug('ET Cells Path: {}'.format(et_cells_path))
    logging.debug('Stations Path: {}'.format(stations_path))

    # Check units
    if station_elev_units.upper() not in ['FEET', 'FT', 'METERS', 'M']:
        logging.error(
            '\nERROR: Station elevation units {} are invalid'
            '\n  Units must be METERS or FEET'.format(station_elev_units))
        sys.exit()

    # Build output table folder if necessary
    if not os.path.isdir(static_ws):
        os.makedirs(static_ws)

    # Read weather station/cell data
    logging.info('\nReading station shapefile')
    logging.debug('  {}'.format(stations_path))
    fields = [station_id_field, station_elev_field,
              station_lat_field, station_lon_field]
    logging.debug('  Fields: {}'.format(fields))
    station_data_dict = defaultdict(dict)
    try:
        for fid, row in _arcpy.search_cursor(stations_path, fields).items():
            # print(fid)
            # print(row)
            # Switch to station_id_field as index (instead of FID)
            for f in fields[1:]:
                station_data_dict[str(row[station_id_field])][f] = row[f]
        for k, v in station_data_dict.items():
            logging.debug('  {}: {}'.format(k, v))
    except:
        logging.error('Expected Field Not Found. Check input shapefile : {}'.format(stations_path))
        return False

    # Read ET Cell zonal stats
    logging.info('\nReading ET Cell Zonal Stats')
    logging.debug('  {}'.format(et_cells_path))
    crop_field_list = sorted([
        f for f in _arcpy.list_fields(et_cells_path)
        if re.match('CROP_\d{2}', f)])
    fields = [cell_id_field, cell_name_field, cell_lat_field, station_id_field,
              awc_in_ft_field, clay_field, sand_field,
              hydgrp_num_field, hydgrp_field]
    fields = fields + crop_field_list
    logging.debug('  Fields: {}'.format(fields))
    cell_data_dict = defaultdict(dict)

    for fid, row in _arcpy.search_cursor(et_cells_path, fields).items():
        # Switch to cell_id_field as index (instead of FID)
        for f in fields[1:]:
            cell_data_dict[str(row[cell_id_field])][f] = row[f]

    # Convert elevation units if necessary
    if station_elev_units.upper() in ['METERS', 'M']:
        logging.debug('  Convert station elevation from meters to feet')
        for k in station_data_dict.keys():
            station_data_dict[k][station_elev_field] /= 0.3048

    logging.info('\nCopying template static files')
    for static_name in static_list:
        # if (overwrite_flag or
        #         os.path.isfile(os.path.join(static_ws, static_name))):
        logging.debug('  {}'.format(static_name))
        shutil.copy(os.path.join(template_ws, static_name), static_ws)
        # shutil.copyfile(
        #     .path.join(template_ws, static_name),
        #     .path.join(static_ws, crop_params_name))

    logging.info('\nWriting static text files')
    cell_props_path = os.path.join(static_ws, cell_props_name)
    cell_crops_path = os.path.join(static_ws, cell_crops_name)
    cell_cuttings_path = os.path.join(static_ws, cell_cuttings_name)
    # crop_params_path = os.path.join(static_ws, crop_params_name)
    # crop_coefs_path = os.path.join(static_ws, crop_coefs_name)
    eto_ratio_path = os.path.join(static_ws, eto_ratio_name)
    etr_ratio_path = os.path.join(static_ws, etr_ratio_name)

    # Write cell properties
    logging.debug('  {}'.format(cell_props_path))
    with open(cell_props_path, 'a') as output_f:
        for cell_id, cell_data in sorted(cell_data_dict.items()):
            try:
                station_id = cell_data[station_id_field]
            except KeyError:
                logging.info(
                    '    {} field was not found in the cell data'.format(
                        station_id_field))

            if station_id:
                #STATION_ID can be either a str or int in the cells .shp
                station_data = station_data_dict[str(station_id)]
                station_lat = '{:>9.4f}'.format(station_data[station_lat_field])
                station_lon = '{:>9.4f}'.format(station_data[station_lon_field])
                station_elev = '{:.2f}'.format(station_data[station_elev_field])
            else:
                station_lat, station_lon, station_elev = '', '', ''
            # There is an extra/unused column in the template and excel files
            output_list = [
                cell_id, cell_data[cell_name_field],
                station_id, station_lat, station_lon,
                station_elev, permeability,
                '{:.4f}'.format(cell_data[awc_in_ft_field]), soil_depth,
                cell_data[hydgrp_field], cell_data[hydgrp_num_field],
                aridity, '']
            output_f.write('\t'.join(map(str, output_list)) + '\n')

            del output_list
            del station_id, station_lat, station_lon, station_elev

    # Write cell crops
    logging.debug('  {}'.format(cell_crops_path))
    with open(cell_crops_path, 'a') as output_f:
        for cell_id, cell_data in sorted(cell_data_dict.items()):
            try:
                station_id = cell_data[station_id_field]
            except KeyError:
                logging.info(
                    '    {} field was not found in the cell data'.format(
                        station_id_field))
                station_id = ''

            output_list = [
                cell_id, cell_data[cell_name_field], station_id, irrigation]
            crop_list = ['CROP_{:02d}'.format(i) for i in range(1, crops + 1)]
            crop_area_list = []
            for crop in crop_list:
                if crop in cell_data.keys() and cell_data[crop] is not None:
                    crop_area_list.append(cell_data[crop])
                else:
                    crop_area_list.append(0)
            crop_flag_list = [
                1 if area > area_threshold else 0 for area in crop_area_list]
            output_list = output_list + crop_flag_list
            output_f.write('\t'.join(map(str, output_list)) + '\n')

            del crop_list, crop_area_list, crop_flag_list, output_list

    # Write cell cuttings
    logging.debug('  {}'.format(cell_cuttings_path))
    with open(cell_cuttings_path, 'a') as output_f:
        for cell_id, cell_data in sorted(cell_data_dict.items()):
            output_list = [
                cell_id, cell_data[cell_name_field],
                '{:>9.4f}'.format(cell_data[cell_lat_field]),
                dairy_cuttings, beef_cuttings]
            output_f.write('\t'.join(map(str, output_list)) + '\n')

    # Write monthly ETo ratios
    logging.debug('  {}'.format(eto_ratio_path))
    with open(eto_ratio_path, 'a') as output_f:
        for cell_id, cell_data in sorted(cell_data_dict.items()):
            try:
                station_id = cell_data[station_id_field]
            except KeyError:
                logging.info(
                    '    {} field was not found in the cell data, '
                    'skipping'.format(station_id_field))
                # station_id = ''
                continue

            output_f.write(
                '\t'.join(map(str, [station_id, ''] + [1.0] * 12)) + '\n')

    # Write monthly ETr ratios
    logging.debug('  {}'.format(etr_ratio_path))
    with open(etr_ratio_path, 'a') as output_f:
        for cell_id, cell_data in sorted(cell_data_dict.items()):
            try:
                station_id = cell_data[station_id_field]
            except KeyError:
                logging.info(
                    '    {} field was not found in the cell data, '
                    'skipping'.format(station_id_field))
                # station_id = ''
                continue

            output_f.write(
                '\t'.join(map(str, [station_id, ''] + [1.0] * 12)) + '\n')

def arg_parse():
    """"""
    parser = argparse.ArgumentParser(
        description='ET-Demands Static Files',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument(
        '-i', '--ini', required=True, metavar='INI',
        type=lambda x: util.is_valid_file(parser, x),
        help='Input file')
    parser.add_argument(
        '--acres', default=10, type=float,
        help='Crop area threshold')
    parser.add_argument(
        '--beef', default=4, type=int,
        help='Number of beef hay cuttings')
    parser.add_argument(
        '--dairy', default=5, type=int,
        help='Number of dairy hay cuttings')
    parser.add_argument(
        '-o', '--overwrite', default=None, action='store_true',
        help='Overwrite existing file')
    parser.add_argument(
        '--debug', default=logging.INFO, const=logging.DEBUG,
        help='Debug level logging', action="store_const", dest="loglevel")
    args = parser.parse_args()

    # Convert input file to an absolute path
    if args.ini and os.path.isdir(os.path.abspath(args.ini)):
        args.ini = os.path.abspath(args.ini)

    return args

if __name__ == '__main__':
    args = arg_parse()

    logging.basicConfig(level=args.loglevel, format='%(message)s')
    logging.info('\n{}'.format('#' * 80))
    logging.info('{0:<20s} {1}'.format(
        'Run Time Stamp:', dt.datetime.now().isoformat(' ')))
    logging.info('{0:<20s} {1}'.format('Current Directory:', os.getcwd()))
    logging.info('{0:<20s} {1}'.format(
        'Script:', os.path.basename(sys.argv[0])))

    main(ini_path=args.ini, area_threshold=args.acres,
         dairy_cuttings=args.dairy, beef_cuttings=args.beef,
         overwrite_flag=args.overwrite)


#-----------------
#|	James Hom   |
#|	jhh318      |
#|	11287431    |
#|	Mark Keil   |
#-----------------

import numpy as np
import time

# start time for the time module
start_time = time.time()
# list of fruits abbreviated
fruits_list = ["F", "W", "G", "J", "M"]
# list of fruit's full names and their indexes in fruits_list to match order in output
fruit_value = [("Waterfruits", 1), ("Grassfruits", 2), ("Megafriuts", 4), ("Firefruits", 0), ("Joltfruits", 3)]

def initial_garden(filename):
    """
    This function takes in the text file and reads all the lines and determines the size of the garden and where each
        fruit is initially placed before starting the first year of spring.

    filename: string; used to access the text file
    return: the initial start for the garden array
    """
    f = open(filename, "r")
    get_line = f.readlines()        # read all the lines in the text file
    initial_tree_coords = {}        # dictionary to keep the coordinates for each fruit
    # create an empty array base off the first line of the text file
    garden_array = np.empty([int(get_line[0]), int(get_line[0])], dtype=str)
    garden_array.fill("_")      # fill the array with "_" to indicate an empty space

    for i in get_line:      # for-loop to iterate through all the lines in the text file
        if i == get_line[1]:        # if i equals the second line in the text file...\
            # create a key-value pair where, {"fruit abbreviation" : [list of tuples as coordinates]}
            initial_tree_coords["F"] = (get_line[1].strip().split(" "))
            # for-loop that iterates through how ever many coordinates there are to change the tuple values from string
            #   to integers
            for k in range(len(initial_tree_coords["F"])):
                initial_tree_coords["F"][k] = tuple(int(j) for j in initial_tree_coords["F"][k].split(","))
                garden_array[initial_tree_coords["F"][k]] = "F"     # implements the coordinates into the garden_array
        if i == get_line[2]:
            initial_tree_coords["W"] = (get_line[2].strip().split(" "))
            for k in range(len(initial_tree_coords["W"])):
                initial_tree_coords["W"][k] = tuple(int(j) for j in initial_tree_coords["W"][k].split(","))
                garden_array[initial_tree_coords["W"][k]] = "W"
        if i == get_line[3]:
            initial_tree_coords["G"] = (get_line[3].strip().split(" "))
            for k in range(len(initial_tree_coords["G"])):
                initial_tree_coords["G"][k] = tuple(int(j) for j in initial_tree_coords["G"][k].split(","))
                garden_array[initial_tree_coords["G"][k]] = "G"
        if i == get_line[4]:
            initial_tree_coords["J"] = (get_line[4].strip().split(" "))
            for k in range(len(initial_tree_coords["J"])):
                initial_tree_coords["J"][k] = tuple(int(j) for j in initial_tree_coords["J"][k].split(","))
                garden_array[initial_tree_coords["J"][k]] = "J"
    f.close()
    return garden_array

def around_fruits(i, j, garden_array):
    """
    This function looks through the garden array and finds the adjacent spaces for each single space in the array to
        place fruit abbreviations in and makes sure to not include spaces that are out of bounds.

    i: int; index of the row of the garden array
    j: int; index of the column of the garden array
    garden_array: array; the initial array of the garden
    return: list of tuples of integers that are possible coordinates within the range of the garden_array
    """
    adjacent_list = []
    for row in range(i - 1, i + 2):       # ranges from within 3 spaces row-wise
        # if the row coordinate is in range of the length of the garden_array...
        if row in range(0, len(garden_array)):
            for col in range(j - 1, j + 2):     # ranges within 3 spaces column-wise
                # if the col coordinate is in range of the length of garden_array...
                if col in range(0, len(garden_array)):
                    if row == i and col == j:
                        continue
                    # add valid adjacent coordinates to adjacent_list
                    adjacent_list.append((row, col))
    return adjacent_list

def fruit_type(adjacent_list, garden_array):
    """
    This function determines and return types of trees in the cells with coordinates from adjacent_list

    adjacent_list: list; list of tuples of integers
    garden_array: array; matrix to work with (list of lists of strings)
    return: list of fruit types coinciding with adjacent_list
    """
    tree_list = []
    for (i, j) in adjacent_list:            # does not include abbreviation for megafruit
        if garden_array[i][j] != "_":       # if the cell is not empty...
            tree_list.append(garden_array[i][j])        # append the fruit abbreviation
    return tree_list        # return unique strings abbreviations of trees from adjacent cells

def fruit_logic(fruit_coordinates):
    """
    Determine what type of tree should be grown among the list given trees using the logic of 'M' > all fruits, 'J' >
        'F', 'W', and 'G', 'F' > 'G', 'W' > 'F', 'G' > 'W', if 'F', 'W', 'G', 'J' compete for same cell, it produces
        a 'M', and if 'F', 'W', and 'G' compete for same cell, it produces '_'.

    fruits: list; trees abbreviations around particular cell
    return: string of fruit abbreviations to grow
    """
    # if there is no fruit in fruits, then produce "_"
    if len(fruit_coordinates) == 0:
        return "_"
    # megafruit dominates all
    elif "M" in fruit_coordinates:
        return "M"
    elif "J" in fruit_coordinates:
        # all 4 fruits: return megafruit
        if "F" in fruit_coordinates and "W" in fruit_coordinates and "G" in fruit_coordinates:
            return "M"
        # joltfruit dominates all except megafruit
        return "J"
    # 3 regular fruits: nothing spreads
    elif "F" in fruit_coordinates and "W" in fruit_coordinates and "G" in fruit_coordinates:
        return "_"
    # waterfruit dominates firefruit
    elif "F" in fruit_coordinates and "W" in fruit_coordinates:
        return "W"
    # firefruit dominates grassfruit
    elif "F" in fruit_coordinates and "G" in fruit_coordinates:
        return "F"
    # grassfruit dominates waterfruit
    elif "W" in fruit_coordinates and "G" in fruit_coordinates:
        return "G"
    # else: only one type of fruit: return that fruit
    return fruit_coordinates[0]

def spring(garden_array):
    """
    This function simulates a one year growth of the garden_array

    garden: array; matrix of fruits
    return: new garden, which grows yearly is applicable
    """
    # copy garden_array to build a new array with yearly planted fruits
    new_garden = garden_array.copy()
    # for each cell in the garden
    for i in range(len(garden_array)):
        for j in range(len(garden_array)):
            # if cell is empty (there is no fruit yet)
            if garden_array[i][j] == "_":
                # get its adjacent cells
                adjacent_fruits = around_fruits(i, j, garden_array)
                fruits = fruit_type(adjacent_fruits, garden_array)
                # determine, which types of fruits current cell is surrounded
                fruit = fruit_logic(fruits)
                # assign new fruit to the cell in new garden or stay empty
                new_garden[i][j] = fruit
    return new_garden

def fall(garden_array, total_fruits):
    """
    This function simulates the harvesting of fruits in the fall

    garden_array: array; a matrix with fruits
    total_fruits: list; list of counts of harvest from previous years
    return: list of integers that represent the total fruits in the garden_array and a list of integers that are
            harvested
    """
    # initiate list with harvest equal to zero to count current year"s harvest
    year_harvest = [0, 0, 0, 0, 0]
    # for each cell in garden
    for i in range(len(garden_array)):
        for j in range(len(garden_array[i])):
            # if there is fruit in the cell
            if garden_array[i][j] != "_":
                # determine its abbreviation index from constant fruits_list
                index = fruits_list.index(garden_array[i][j])
                # add 1 to the value with index "index" in year harvest and total harvest lists
                year_harvest[index] += 1
                total_fruits[index] += 1
    return total_fruits, year_harvest

def grow_garden(garden_array):
    """
    This function simulates growing the garden within all of the years it can grow

    garden_array: array; matrix with fruits (list of lists of strings)
    """
    # initiate list with harvest equal to zero to count all years harvest
    total_fruits = [0, 0, 0, 0, 0]
    # assign years counter to 0
    years = 0
    # while garden is growing
    while True:
        # get an updated garden_array based off from the spring function
        spring_garden = spring(garden_array.copy())
        # count yearly and total harvest
        if years > 0:
            total_fruits, year_harvest = fall(garden_array, total_fruits)
        # if garden does not grow anymore return lists with harvests and years counter
        if np.all(garden_array == spring_garden) and years > 0:
            return total_fruits, year_harvest, years
        # in other go to next year
        years += 1
        # update garden
        garden_array = spring_garden

def harvest():
    """
    This function goes through each text file and performs all of the function prior to harvest() and prints out the
        dedicated results
    """
    text_files = ["pokefruit_celadonfarm.txt", "pokefruit_palletfarm.txt", "pokefruit_pewterfarm.txt",
                  "pokefruit_saffronfarm.txt", "pokefruit_viridianfarm.txt"]
    for file in text_files:
        farm_array = initial_garden(file)
        total_yield, final_year_harvest, total_years = grow_garden(farm_array)
        # print summary
        print("Fruits yield from final year:")
        print("****************************")
        for (fruit, x) in fruit_value:
            print(fruit, ":", final_year_harvest[x])
        print()
        print("Total farm yield after", total_years, "years:")
        print("**********************************")
        for (fruit, x) in fruit_value:
            print(fruit, ":", total_yield[x])
        print()
### ADD THE '#' BELOW TO GO THROUGH THE TESTING ###
harvest()
# prints the program from start to finish
#print(time.time() - start_time)


from typing import Dict, Union, Tuple, Any

from django.http import JsonResponse

from utils.data import verify_data, verify_dict

def verify_city(data: Dict[str, Any]) -> Union[Tuple[bool, None], Tuple[bool, JsonResponse]]:
    name = data.get('name')

    is_valid, error_message = verify_data(data=name, required=True, data_type=str, max_length=40, error_messages={
        'required': 'Қала атауы міндетті өріс',
        'data_type': 'Қала атауы string типінде болу керек',
        'max_length': 'Қала атауының ұзындығы 40-ден артық',
    })

    if not is_valid:
        return False, JsonResponse({'message': error_message}, status=400)

    is_valid_or_key_name, reason_or_none = verify_dict(
        data, [{'key_name': 'name'}])

    if is_valid_or_key_name != True:
        return False, JsonResponse({'message': reason_or_none}, status=400)

    return True, None

def verify_dorm(data: Dict[str, Any]) -> Union[Tuple[bool, None], Tuple[bool, JsonResponse]]:
    name = data.get('name')
    desciption = data.get('desciption')
    city = data.get('city')
    address = data.get('address')
    images = data.get('images')

    for is_valid, error_message in (verify_data(data=name, required=True, data_type=str, max_length=40, error_messages={
        'required': 'Жатақхана атауы міндетті өріс',
        'data_type': 'Жатақхана атауы string типінде болу керек',
        'max_length': 'Жатақхана атауының ұзындығы 40-ден артық',
    }), verify_data(data=desciption, required=False, data_type=str, max_length=254, error_messages={
        'data_type': 'Жатақхана сипаттамасы string типінде болу керек',
        'max_length': 'Жатақхана сипаттамасының ұзындығы 254-ден артық',
    }), verify_data(data=city, required=True, data_type=int, error_messages={
        'required': 'Жатақхананың орналасқан қаласы міндетті өріс',
        'data_type': 'Жатақхананың орналасқан қаласы int типінде болу керек',
    }), verify_data(data=address, required=True, data_type=str, max_length=60, error_messages={
        'required': 'Жатақхананың нақты мекен-жайы міндетті өріс',
        'data_type': 'Жатақхананың нақты мекен-жайы string типінде болу керек',
        'max_length': 'Жатақхананың нақты мекен-жайының ұзындығы 60-ден артық',
    })):
        if not is_valid:
            return False, JsonResponse({'message': error_message}, status=400)

    is_valid_or_key_name, reason_or_none = verify_dict(data, [{'key_name': 'name'}, {'key_name': 'description', 'required': False}, {
        'key_name': 'city'}, {'key_name': 'address'}])

    if is_valid_or_key_name != True:
        return False, JsonResponse({'message': reason_or_none}, status=400)

    return True, None


from __future__ import absolute_import

import contextlib
import anydbm
from itertools import chain
import re

from mercurial import (
    bookmarks,
    cmdutil,
    commands,
    dagop,
    error,
    extensions,
    graphmod,
    obsutil,
    phases,
    registrar,
    revlog,
    revset,
    revsetlang,
    scmutil,
    smartset,
    templatekw,
    util,
)
from mercurial import node as nodemod
from mercurial.i18n import _

cmdtable = {}
command = registrar.command(cmdtable)
testedwith = 'ships-with-fb-hgext'
commit_info = False
hiddenchanges = 0

# Remove unsupported --limit option.
logopts = [opt for opt in commands.logopts if opt[1] != "limit"]

@contextlib.contextmanager
def ancestorcache(path):
    # simple cache to speed up revlog.ancestors
    try:
        db = anydbm.open(path, 'c')
    except anydbm.error:
        # database locked, fail gracefully
        yield
    else:
        def revlogancestor(orig, self, a, b):
            key = a + b
            try:
                return db[key]
            except KeyError:
                result = orig(self, a, b)
                db[key] = result
                return result
        extensions.wrapfunction(revlog.revlog, 'ancestor', revlogancestor)
        try:
            yield
        finally:
            extensions.unwrapfunction(revlog.revlog, 'ancestor', revlogancestor)
            try:
                db.close()
            except Exception:
                # database corruption, we just nuke the database
                util.tryunlink(path)

def _drawendinglines(orig, lines, extra, edgemap, seen):
    # if we are going to have only one single column, draw the missing '|'s
    # and restore everything to normal. see comment in 'ascii' below for an
    # example of what will be changed. note: we do not respect 'graphstyle'
    # but always draw '|' here, for simplicity.
    if len(seen) == 1 or any(l[0:2] != [' ', ' '] for l in lines):
        # draw '|' from bottom to top in the 1st column to connect to
        # something, like a '/' in the 2nd column, or a '+' in the 1st column.
        for line in reversed(lines):
            if line[0:2] != [' ', ' ']:
                break
            line[0] = '|'
        # undo the wrapfunction
        extensions.unwrapfunction(graphmod, '_drawendinglines',
                                  _drawendinglines)
        # restore the space to '|'
        for k, v in edgemap.iteritems():
            if v == ' ':
                edgemap[k] = '|'
    orig(lines, extra, edgemap, seen)

def uisetup(ui):
    # Hide output for fake nodes
    def show(orig, self, ctx, copies, matchfn, props):
        if ctx.node() == "...":
            self.ui.write('\n\n\n')
            return
        res = orig(self, ctx, copies, matchfn, props)

        if commit_info and ctx == self.repo['.']:
            changes = ctx.p1().status(ctx)
            prefixes = ['M', 'A', 'R', '!', '?', 'I', 'C']
            for prefix, change in zip(prefixes, changes):
                for fname in change:
                    self.ui.write(' {0} {1}\n'.format(prefix, fname))
            self.ui.write('\n')
        return res

    extensions.wrapfunction(cmdutil.changeset_printer, '_show', show)
    extensions.wrapfunction(cmdutil.changeset_templater, '_show', show)

    def ascii(orig, ui, state, type, char, text, coldata):
        if type == 'F':
            # the fake node is used to move draft changesets to the 2nd column.
            # there can be at most one fake node, which should also be at the
            # top of the graph.
            # we should not draw the fake node and its edges, so change its
            # edge style to a space, and return directly.
            # these are very hacky but it seems to work well and it seems there
            # is no other easy choice for now.
            edgemap = state['edges']
            for k in edgemap.iterkeys():
                edgemap[k] = ' '
            # also we need to hack _drawendinglines to draw the missing '|'s:
            #    (before)      (after)
            #     o draft       o draft
            #    /             /
            #                 |
            #   o             o
            extensions.wrapfunction(graphmod, '_drawendinglines',
                                    _drawendinglines)
            return
        orig(ui, state, type, char, text, coldata)

    extensions.wrapfunction(graphmod, 'ascii', ascii)

    revset.symbols['smartlog'] = smartlogrevset
    revset.safesymbols.add('smartlog')

templatekeyword = registrar.templatekeyword()

@templatekeyword('singlepublicsuccessor')
def singlepublicsuccessor(repo, ctx, templ, **args):
    """String. Get a single public successor for a
    given node.  If there's none or more than one, return empty string.
    This is intended to be used for "Landed as" marking
    in `hg sl` output."""
    successorssets = obsutil.successorssets(repo, ctx.node())
    unfiltered = repo.unfiltered()
    ctxs = (unfiltered[n] for n in chain.from_iterable(successorssets))
    public = (c.hex() for c in ctxs if not c.mutable() and c != ctx)
    first = next(public, '')
    second = next(public, '')

    return '' if first and second else first

@templatekeyword('rebasesuccessors')
def rebasesuccessors(repo, ctx, **args):
    """Return all of the node's successors created as a result of rebase"""
    rsnodes = list(modifysuccessors(ctx, 'rebase'))
    return templatekw.showlist('rebasesuccessor', rsnodes, args)

@templatekeyword('amendsuccessors')
def amendsuccessors(repo, ctx, **args):
    """Return all of the node's successors created as a result of amend"""
    asnodes = list(modifysuccessors(ctx, 'amend'))
    return templatekw.showlist('amendsuccessor', asnodes, args)

@templatekeyword('splitsuccessors')
def splitsuccessors(repo, ctx, **args):
    """Return all of the node's successors created as a result of split"""
    asnodes = list(modifysuccessors(ctx, 'split'))
    return templatekw.showlist('splitsuccessor', asnodes, args)

@templatekeyword('foldsuccessors')
def foldsuccessors(repo, ctx, **args):
    """Return all of the node's successors created as a result of fold"""
    asnodes = list(modifysuccessors(ctx, 'fold'))
    return templatekw.showlist('foldsuccessor', asnodes, args)

@templatekeyword('histeditsuccessors')
def histeditsuccessors(repo, ctx, **args):
    """Return all of the node's successors created as a result of
       histedit
    """
    asnodes = list(modifysuccessors(ctx, 'histedit'))
    return templatekw.showlist('histeditsuccessor', asnodes, args)

@templatekeyword('undosuccessors')
def undosuccessors(repo, ctx, **args):
    """Return all of the node's successors created as a result of undo"""
    asnodes = list(modifysuccessors(ctx, 'undo'))
    return templatekw.showlist('undosuccessor', asnodes, args)

def successormarkers(ctx):
    for data in ctx.repo().obsstore.successors.get(ctx.node(), ()):
        yield obsutil.marker(ctx.repo(), data)

def modifysuccessors(ctx, operation):
    """Return all of the node's successors which were created as a result
    of a given modification operation"""
    repo = ctx.repo().filtered('visible')
    for m in successormarkers(ctx):
        if m.metadata().get('operation') == operation:
            for node in m.succnodes():
                try:
                    repo[node]
                except Exception:
                    # filtered or unknown node
                    pass
                else:
                    yield nodemod.hex(node)

def sortnodes(nodes, parentfunc, masters):
    """Topologically sorts the nodes, using the parentfunc to find
    the parents of nodes.  Given a topological tie between children,
    any node in masters is chosen last."""
    nodes = set(nodes)
    childmap = {}
    parentmap = {}
    roots = []

    # Build a child and parent map
    for n in nodes:
        parents = [p for p in parentfunc(n) if p in nodes]
        parentmap[n] = set(parents)
        for p in parents:
            childmap.setdefault(p, set()).add(n)
        if not parents or (len(parents) == 1 and parents[0] == -1) and n != -1:
            roots.append(n)

    def childsortkey(x):
        # Process children in the master line last. This makes them always
        # appear on the left side of the dag, resulting in a nice straight
        # master line in the ascii output. Otherwise show the oldest first, so
        # the graph is approximately in chronological order.
        return (x in masters, x)

    # Process roots, adding children to the queue as they become roots
    results = []
    while roots:
        n = roots.pop(0)
        results.append(n)
        if n in childmap:
            children = list(childmap[n])
            # reverse=True here because we insert(0) below, resulting
            # in a reversed insertion of the children.
            children = sorted(children, reverse=True, key=childsortkey)
            for c in children:
                childparents = parentmap[c]
                childparents.remove(n)
                if len(childparents) == 0:
                    # insert at the beginning, that way child nodes
                    # are likely to be output immediately after their
                    # parents.
                    roots.insert(0, c)

    return results

def getdag(ui, repo, revs, master):

    # Fake ctx that we stick in the dag so we can special case it later
    class fakectx(object):
        def __init__(self, rev):
            self._rev = rev
        def node(self):
            return "..."
        def obsolete(self):
            return False
        def phase(self):
            return None
        def rev(self):
            return self._rev
        def files(self):
            return []
        def closesbranch(self):
            return False

    knownrevs = set(revs)
    gpcache = {}
    results = []

    # we store parents together with the parent type information
    # but sometimes we need just a list of parents
    # [(a,b), (c,d), (e,f)] => [b, d, f]
    def unzip(parents):
        if parents:
            return list(zip(*parents)[1])
        else:
            return list()

    # For each rev we need to show, compute it's parents in the dag.
    # If we have to reach for a grandparent, insert a fake node so we
    # can show '...' in the graph.
    # Use 'reversed' to start at the lowest commit so fake nodes are
    # placed at their lowest possible positions.
    for rev in reversed(revs):
        ctx = repo[rev]
        # Parents in the dag
        parents = sorted(set([(graphmod.PARENT, p.rev()) for p in ctx.parents()
                              if p.rev() in knownrevs]))
        # Parents not in the dag
        mpars = [p.rev() for p in ctx.parents() if
                 p.rev() != nodemod.nullrev and p.rev() not in unzip(parents)]

        for mpar in mpars:
            gp = gpcache.get(mpar)
            if gp is None:
                gp = gpcache[mpar] = dagop.reachableroots(
                    repo, smartset.baseset(revs), [mpar])
            if not gp:
                parents.append((graphmod.MISSINGPARENT, mpar))
            else:
                gp = [g for g in gp if g not in unzip(parents)]
                for g in gp:
                    parents.append((graphmod.GRANDPARENT, g))

        results.append((ctx.rev(), 'C', ctx, parents))

    # Compute parent rev->parents mapping
    lookup = {}
    for r in results:
        lookup[r[0]] = unzip(r[3])

    def parentfunc(node):
        return lookup.get(node, [])

    # Compute the revs on the master line. We use this for sorting later.
    masters = set()
    queue = [master]
    while queue:
        m = queue.pop()
        if m not in masters:
            masters.add(m)
            queue.extend(lookup.get(m, []))

    # Topologically sort the noderev numbers. Note: unlike the vanilla
    # topological sorting, we move master to the top.
    order = sortnodes([r[0] for r in results], parentfunc, masters)
    order = dict((e[1], e[0]) for e in enumerate(order))

    # Sort the actual results based on their position in the 'order'
    try:
        results.sort(key=lambda x: order[x[0]], reverse=True)
    except ValueError:  # Happened when 'order' is empty
        msg = _('note: smartlog encountered an error\n')
        hint = _('(so the sorting might be wrong.\n\n)')
        ui.warn(msg)
        ui.warn(hint)
        results.reverse()

    # indent the top non-public stack
    if ui.configbool('smartlog', 'indentnonpublic', False):
        rev, ch, ctx, parents = results[0]
        if ctx.phase() != phases.public:
            # find a public parent and add a fake node, so the non-public nodes
            # will be shown in the non-first column
            prev = None
            for i in xrange(1, len(results)):
                pctx = results[i][2]
                if pctx.phase() == phases.public:
                    prev = results[i][0]
                    break
            # append the fake node to occupy the first column
            if prev:
                fakerev = rev + 1
                results.insert(0, (fakerev, 'F', fakectx(fakerev),
                                   [('P', prev)]))

    return results

def _masterrevset(ui, repo, masterstring):
    """
    Try to find the name of ``master`` -- usually a bookmark.

    Defaults to the last public revision, if no suitable local or remote
    bookmark is found.
    """

    if not masterstring:
        masterstring = ui.config('smartlog', 'master')

    if masterstring:
        return masterstring

    names = set(bookmarks.bmstore(repo).keys())
    if util.safehasattr(repo, 'names') and 'remotebookmarks' in repo.names:
        names.update(set(repo.names['remotebookmarks'].listnames(repo)))

    for name in _reposnames(ui):
        if name in names:
            return name

    return 'last(public())'

def _reposnames(ui):
    # '' is local repo. This also defines an order precedence for master.
    repos = ui.configlist('smartlog', 'repos', ['', 'remote/', 'default/'])
    names = ui.configlist('smartlog', 'names', ['@', 'master', 'stable'])

    for repo in repos:
        for name in names:
            yield repo + name

def _masterrev(repo, masterrevset):
    try:
        master = scmutil.revsingle(repo, masterrevset)
    except error.RepoLookupError:
        master = scmutil.revsingle(repo, _masterrevset(repo.ui, repo, ''))
    except error.Abort:  # empty revision set
        return None

    if master:
        return master.rev()
    return None

def smartlogrevset(repo, subset, x):
    """``smartlog([master], [recentdays=N])``
    Changesets relevent to you.

    'master' is the head of the public branch.
    Unnamed heads will be hidden unless it's within 'recentdays'.
    """

    args = revset.getargsdict(x, 'smartlogrevset', 'master recentdays')
    if 'master' in args:
        masterstring = revsetlang.getstring(args['master'],
                                            _('master must be a string'))
    else:
        masterstring = ''

    recentdays = revsetlang.getinteger(args.get('recentdays'),
                                       _("recentdays should be int"), -1)

    revs = set()
    heads = set()

    rev = repo.changelog.rev
    branchinfo = repo.changelog.branchinfo
    ancestor = repo.changelog.ancestor
    node = repo.changelog.node
    parentrevs = repo.changelog.parentrevs

    books = bookmarks.bmstore(repo)
    ignore = re.compile(repo.ui.config('smartlog',
                                       'ignorebookmarks',
                                       '!'))
    for b in books:
        if not ignore.match(b):
            heads.add(rev(books[b]))

    # add 'interesting' remote bookmarks as well
    remotebooks = set()
    if util.safehasattr(repo, 'names') and 'remotebookmarks' in repo.names:
        ns = repo.names['remotebookmarks']
        remotebooks = set(ns.listnames(repo))
        for name in _reposnames(repo.ui):
            if name in remotebooks:
                heads.add(rev(ns.namemap(repo, name)[0]))

    heads.update(repo.revs('.'))

    global hiddenchanges
    headquery = 'head() & branch(.)'
    if remotebooks:
        # When we have remote bookmarks, only show draft heads, since public
        # heads should have a remote bookmark indicating them. This allows us
        # to force push server bookmarks to new locations, and not have the
        # commits clutter the user's smartlog.
        headquery = 'draft() &' + headquery

    allheads = set(repo.revs(headquery))
    if recentdays >= 0:
        recentquery = revsetlang.formatspec('%r & date(-%d)', headquery,
                                            recentdays)
        recentrevs = set(repo.revs(recentquery))
        hiddenchanges += len(allheads - heads) - len(recentrevs - heads)
        heads.update(recentrevs)
    else:
        heads.update(allheads)

    branches = set()
    for head in heads:
        branches.add(branchinfo(head)[0])

    masterrevset = _masterrevset(repo.ui, repo, masterstring)
    masterrev = _masterrev(repo, masterrevset)

    if masterrev is None:
        masterbranch = None
    else:
        masterbranch = branchinfo(masterrev)[0]

    for branch in branches:
        if branch != masterbranch:
            try:
                rs = 'first(reverse(branch("%s")) & public())' % branch
                branchmaster = repo.revs(rs).first()
                if branchmaster is None:
                    # local-only (draft) branch
                    rs = 'branch("%s")' % branch
                    branchmaster = repo.revs(rs).first()
            except Exception:
                branchmaster = repo.revs('tip').first()
        else:
            branchmaster = masterrev

        # Find all draft ancestors and latest public ancestor of heads
        # that are not in master.
        # We don't want to draw all public commits because there can be too
        # many of them.
        # Don't use revsets, they are too slow
        for head in heads:
            if branchinfo(head)[0] != branch:
                continue
            anc = rev(ancestor(node(head), node(branchmaster)))
            queue = [head]
            while queue:
                current = queue.pop(0)
                if current not in revs:
                    revs.add(current)
                    # stop as soon as we find public commit
                    ispublic = repo[current].phase() == phases.public
                    if current != anc and not ispublic:
                        parents = parentrevs(current)
                        for p in parents:
                            if p > anc:
                                queue.append(p)

        # add context: master, current commit, and the common ancestor
        revs.add(branchmaster)

        # get common branch ancestor
        if branch != masterbranch:
            anc = None
            for r in revs:
                if branchinfo(r)[0] != branch:
                    continue
                if anc is None:
                    anc = r
                else:
                    anc = rev(ancestor(node(anc), node(r)))
            if anc:
                revs.add(anc)

    return subset & revs

@command('^smartlog|slog', [
    ('', 'template', '', _('display with template'), _('TEMPLATE')),
    ('', 'master', '', _('master bookmark'), ''),
    ('r', 'rev', [], _('show the specified revisions or range'), _('REV')),
    ('', 'all', False, _('don\'t hide old local changesets'), ''),
    ('', 'commit-info', False, _('show changes in current changeset'), ''),
] + logopts, _('hg smartlog|slog'))
def smartlog(ui, repo, *pats, **opts):
    '''displays the graph of changesets that are relevant to you

Includes:

- Your bookmarks
- The @ or master bookmark (or tip if no bookmarks present).
- Your local heads that don't have bookmarks.

Excludes:

- All changesets under @/master/tip that aren't related to your changesets.
- Your local heads that are older than 2 weeks.
    '''
    if ui.configbool('smartlog', 'useancestorcache'):
        with ancestorcache(repo.vfs.join('cache/smartlog-ancestor')):
            return _smartlog(ui, repo, *pats, **opts)
    else:
        return _smartlog(ui, repo, *pats, **opts)

def _smartlog(ui, repo, *pats, **opts):
    masterstring = opts.get('master')
    masterrevset = _masterrevset(ui, repo, masterstring)

    revs = set()

    global hiddenchanges
    hiddenchanges = 0

    global commit_info
    commit_info = opts.get('commit_info')

    if not opts.get('rev'):
        if opts.get('all'):
            recentdays = -1
        else:
            recentdays = 14
        revstring = revsetlang.formatspec('smartlog(%s, %s)', masterrevset,
                                          recentdays)
        revs.update(scmutil.revrange(repo, [revstring]))
        masterrev = _masterrev(repo, masterrevset)
    else:
        revs.update(scmutil.revrange(repo, opts.get('rev')))
        try:
            masterrev = repo.revs('.').first()
        except error.RepoLookupError:
            masterrev = revs[0]

    if -1 in revs:
        revs.remove(-1)

    # It's important that these function caches come after the revsets above,
    # because the revsets may cause extra nodes to become visible, which in
    # turn invalidates the changelog instance.
    rev = repo.changelog.rev
    ancestor = repo.changelog.ancestor
    node = repo.changelog.node

    # Find lowest common ancestors of revs. If we have multiple roots in the
    # repo the following will find one ancestor per group of revs with the
    # same root.
    ancestors = set()
    for r in revs:
        added = False
        for anc in list(ancestors):
            lca = rev(ancestor(node(anc), node(r)))
            if lca != -1:
                if anc != lca:
                    ancestors.discard(anc)
                    ancestors.add(lca)
                added = True

        if not added:
            ancestors.add(r)

    revs |= ancestors

    revs = sorted(list(revs), reverse=True)

    if len(revs) == 0:
        return

    # Print it!
    overrides = {}
    if ui.config('experimental', 'graphstyle.grandparent') == '|':
        overrides[('experimental', 'graphstyle.grandparent')] = '2.'
    with ui.configoverride(overrides, 'smartlog'):
        revdag = getdag(ui, repo, revs, masterrev)
        displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
        ui.pager('smartlog')
        cmdutil.displaygraph(
            ui, repo, revdag, displayer, graphmod.asciiedges, None, None)

    try:
        with open(repo.vfs.join('completionhints'), 'w+') as f:
            for rev in revdag:
                commit_hash = rev[2].node()
                # Skip fakectxt nodes
                if commit_hash != '...':
                    f.write(nodemod.short(commit_hash) + '\n')
    except IOError:
        # No write access. No big deal.
        pass

    if hiddenchanges:
        msg = _(
            "note: hiding %s old heads without bookmarks\n") % hiddenchanges
        hint = _("(use --all to see them)\n")
        ui.warn(msg)
        ui.warn(hint)


def main():
    with open('input.txt','r') as infile:
        bin_nums = [line.strip() for line in infile.readlines()]

    bit_len = len(bin_nums[0])

    gamma_rate = []
    epsilon_rate = []

    for i in range(0,bit_len):
        one_count, zero_count = (0,0)
        this_pos_bits = [num[i] for num in bin_nums]
        one_count = this_pos_bits.count('1')
        zero_count = this_pos_bits.count('0')

        if one_count > zero_count:
            gamma_rate.append('1')
            epsilon_rate.append('0')
        else:
            gamma_rate.append('0')
            epsilon_rate.append('1')

    gamma_rate = ''.join(gamma_rate)
    epsilon_rate = ''.join(epsilon_rate)

    print(int(gamma_rate,2)*int(epsilon_rate,2))

if __name__ == '__main__':
    main()


from django.views.generic import TemplateView
import os
from django.shortcuts import render
from config import settings
import csv
import requests
import uuid
from django.http import HttpResponse
from datetime import datetime
from .models import StarWarsData

class IndexView(TemplateView):
    template_name = 'base.html'

def list_csv_files(request):
    csv_folder_path = os.path.join(settings.MEDIA_ROOT, 'csv_files')
    csv_files = [f for f in os.listdir(csv_folder_path) if f.endswith('.csv')]
    context = {
        "data" : csv_files
    }
    return render(request, 'index.html', context)

def download_csv(request):
    response = requests.get('https://swapi.dev/api/people')
    data = response.json().get('results', [])
    unique_filename = f'{uuid.uuid4()}.csv'

    StarWarsData(filename=unique_filename, data=data).save()

    csv_folder_path = os.path.join(settings.MEDIA_ROOT, 'csv_files')
    csv_file_path = os.path.join(csv_folder_path, unique_filename)
    with open(csv_file_path, mode='w', newline='') as csv_file:
        writer = csv.writer(csv_file)
        for item in data:
            writer.writerow([item.get('name', ''), item.get('height', ''), item.get('mass', ''),
                            item.get('hair_color', ''), item.get('skin_color', ''), item.get('eye_color', ''),
                            item.get('birth_year', ''), item.get('gender', ''), parse_homeword(item.get('homeworld', '')),
                            datetime.now().strftime("%Y-%m-%d")
                            ])
    return HttpResponse(status=200)

def display_csv_content(request, file_name):
    csv_folder_path = os.path.join(settings.MEDIA_ROOT, 'csv_files')
    csv_file_path = os.path.join(csv_folder_path, file_name)

    with open(csv_file_path, 'r', newline='') as csv_file:
        reader = csv.reader(csv_file)
        csv_data = list(reader)

    context = {
        'file_name': file_name,
        'csv_data': csv_data,
    }
    return render(request, 'display_csv_content.html', context)

def parse_homeword(url):
    response = requests.get(url)
    data = response.json()
    return data.get("name", "")

def save_to_db(item):
    pass


from django.shortcuts import render
from django.core.mail import EmailMessage
from django.template.loader import render_to_string
from .models import *
from .forms import *
from dashboard.models import HostelAnnouncements
from django.contrib.auth.decorators import login_required
from api_integration.models import Student
import datetime
from api_integration.models import Student
import os

@login_required
def get_admin_status(request):
	st = 0
	dr = Student.objects.filter(student_first_name = str(request.user))
	if(len(dr) == 0):
            return 0
	print(dr, "ASDAS")
	if(dr[0].is_hostel_admin == True):
		st = 1
	elif(dr[0].is_mess_admin == True):
		st = 2
	elif(dr[0].is_medical_admin == True):
		st = 3
	return st

# Create your views here.

@login_required
def hostel_dashboard(request):
	h_announce = HostelAnnouncements.objects.all().filter(isDeleted=0)
	scrollL_announce = len(h_announce)
	if scrollL_announce <= 3:
		scrollL_announce = '140px'
	else:
		scrollL_announce = str(3 * 70)+ 'px'

	self_help = SelfHelpGroup.objects.all().filter(isDeleted=0)
	scrollL_self = len(self_help)

	if scrollL_self <= 3:
		scrollL_self = '70px'
	else:
		scrollL_self = str(3 * 70)+ 'px'

	courier_data = Courrier.objects.all().filter(isDeleted=0)
	#.filter(student=Student.objects.get(user = request.user))
	courier_data = list(filter(lambda x: x.student.user == request.user, courier_data))
	scrollL_courier = len(courier_data)
	if scrollL_courier <= 3:
		scrollL_courier = '140px'
	else:
		scrollL_courier = str(3 * 70)+ 'px'
	context = {'h_announce':h_announce , 'self_help':self_help , 'courier_data':courier_data,
	'scrollL_announce':scrollL_announce,'scrollL_self':scrollL_self,'scrollL_courier':scrollL_courier,'admin_status': get_admin_status(request)}
	return render(request,'hostel/hostel_dashboard.html',context)

@login_required
def leave_form(request):
	form = HostelLeaveForm()
	return render(request,'hostel/leave.html',{'form':form,'admin_status': get_admin_status(request)})

@login_required
def complaint_form(request):
	form = HostelComplaintForm()
	return render(request,'hostel/complaint.html',{'form':form,'admin_status': get_admin_status(request)})

'''@login_required
def complaint_ack(request):
	return render(request,'hostel/complaint-ack.html')'''

def getDate(dat):
	dat = dat.split('-')
	year = int(dat[0])
	month = int(dat[1])
	day = int(dat[2])

	return datetime.date(year,month,day)

@login_required
def addComplaint(request):
	if request.method == 'POST':
		form = HostelComplaintForm(request.POST,request.FILES)
		if form.is_valid():
			complaint_form = form.save(commit=False)
			complaint_form.student = Student.objects.get(user = request.user)
			subject = request.POST['subject']
			comp = subject +' '+request.POST['complaint']
			checkVal = vulgarCheck(comp)
			print(checkVal)
			if(checkVal==0):
				complaint_form.complaint = comp
			elif(checkVal==1):
				return render(request,'hostel/vulgar-ack.html',{'admin_status': get_admin_status(request)})

			if len(request.FILES) > 0:
				complaint_form.comp_img = request.FILES['image']
			else:
				complaint_form.comp_img = ""
			complaint_form.room_no = request.POST['room_no']
			complaint_form.completed = False
			complaint_form.timestamp = datetime.datetime.now()
			complaint_form.created_at = datetime.datetime.now().date()
			complaint_form.created_by = Student.objects.get(user = request.user)
			complaint_form.modified_at = datetime.datetime.now().date()
			complaint_form.modified_by = Student.objects.get(user = request.user)
			complaint_form.save()
			return render(request,'hostel/complaint-ack.html',{'admin_status': get_admin_status(request)})
	else:
		form =  HostelComplaintForm()
		return render(request,'hostel/complaint.html',{'form':form,'error_message':'','admin_status': get_admin_status(request)})

@login_required
def applyHostelLeave(request):
	if request.method == 'POST':
		form = HostelLeaveForm(request.POST)
		if form.is_valid():
			leave_form = form.save(commit=False)
			leave_form.student = Student.objects.get(user = request.user)
			leave_form.leave_from = getDate(request.POST['leave_from'])
			leave_form.leave_to = getDate(request.POST['leave_to'])
			leave_form.timestamp = datetime.datetime.now()
			leave_form.created_at = datetime.datetime.now().date()
			leave_form.modified_at = datetime.datetime.now().date()

			leave_form.created_by = Student.objects.get(user = request.user)
			leave_form.modified_by = Student.objects.get(user = request.user)

			mess_leave = request.POST['select_opt']
#--------------------------------------------------------------------------------------------
			if(mess_leave=='Yes'):
				#Update the mess models with creating an instance of the mess models
				print(mess_leave)
#--------------------------------------------------------------------------------------------
			if(leave_form.leave_from >=  leave_form.leave_to):
				error_message = "Please enter valid From and TO dates"
				return render(request,'hostel/leave.html',{'form':form,'error_message':error_message})
			leave_form.save()
			return render(request, 'hostel/leave-ack.html',{'admin_status': get_admin_status(request)})
		else:
			error_message = "Please enter data in YYYY-MM-DD format"
			return render(request,'hostel/leave.html',{'form':form,'error_message':error_message,'admin_status': get_admin_status(request)})

	form =  HostelLeaveForm()
	return render(request,'hostel/leave.html',{'form':form,'error_message':'','admin_status': get_admin_status(request)})

def vulgarCheck(s):
	vul = dict()
	with open(os.getcwd()+"/vulgar.txt",'r') as file:
		text = file.readlines()
		for i in range(len(text)):
			text[i]=text[i].strip()
			if(text[i] not in vul):
				vul[text[i]] = 1

	s = s.split(' ')
	for j in range(len(s)):
		if(s[j] in vul):
			return 1

		if(j>0):
			if((s[j-1]+' '+s[j]) in vul):
				return 1

		if(j>1):
			if((s[j-2]+' '+s[j-1]+' '+s[j]) in vul):
				return 1

		if(j>2):
			if((s[j-3]+' '+s[j-2]+' '+s[j-1]+' '+s[j]) in vul):
				return 1

	return 0