from packaging.version import Version
import os

import random

import numpy as np
import pandas as pd

from shapely.geometry import Point, Polygon, LineString
import pyproj

from geopandas import GeoSeries, GeoDataFrame, points_from_xy, datasets, read_file
from geopandas.array import from_shapely, from_wkb, from_wkt, GeometryArray

from geopandas.testing import assert_geodataframe_equal
import pytest

# pyproj 2.3.1 fixed a segfault for the case working in an environment with
# 'init' dicts (https://github.com/pyproj4/pyproj/issues/415)
PYPROJ_LT_231 = Version(pyproj.__version__) < Version("2.3.1")

def _create_df(x, y=None, crs=None):
    y = y or x
    x = np.asarray(x)
    y = np.asarray(y)

    return GeoDataFrame(
        {"geometry": points_from_xy(x, y), "value1": x + y, "value2": x * y}, crs=crs
    )

def df_epsg26918():
    # EPSG:26918
    # Center coordinates
    # -1683723.64 6689139.23
    return _create_df(
        x=range(-1683723, -1683723 + 10, 1),
        y=range(6689139, 6689139 + 10, 1),
        crs="epsg:26918",
    )

def test_to_crs_transform():
    df = df_epsg26918()
    lonlat = df.to_crs(epsg=4326)
    utm = lonlat.to_crs(epsg=26918)
    assert_geodataframe_equal(df, utm, check_less_precise=True)

def test_to_crs_transform__missing_data():
    # https://github.com/geopandas/geopandas/issues/1573
    df = df_epsg26918()
    df.loc[3, "geometry"] = None
    lonlat = df.to_crs(epsg=4326)
    utm = lonlat.to_crs(epsg=26918)
    assert_geodataframe_equal(df, utm, check_less_precise=True)

def test_to_crs_inplace():
    df = df_epsg26918()
    lonlat = df.to_crs(epsg=4326)
    df.to_crs(epsg=4326, inplace=True)
    assert_geodataframe_equal(df, lonlat, check_less_precise=True)

def test_to_crs_geo_column_name():
    # Test to_crs() with different geometry column name (GH#339)
    df = df_epsg26918()
    df = df.rename(columns={"geometry": "geom"})
    df.set_geometry("geom", inplace=True)
    lonlat = df.to_crs(epsg=4326)
    utm = lonlat.to_crs(epsg=26918)
    assert lonlat.geometry.name == "geom"
    assert utm.geometry.name == "geom"
    assert_geodataframe_equal(df, utm, check_less_precise=True)

# -----------------------------------------------------------------------------
# Test different supported formats for CRS specification

@pytest.fixture(
    params=[
        4326,
        "epsg:4326",
        pytest.param(
            {"init": "epsg:4326"},
            marks=pytest.mark.skipif(PYPROJ_LT_231, reason="segfault"),
        ),
        "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs",
        {"proj": "latlong", "ellps": "WGS84", "datum": "WGS84", "no_defs": True},
    ],
    ids=["epsg_number", "epsg_string", "epsg_dict", "proj4_string", "proj4_dict"],
)
def epsg4326(request):
    if isinstance(request.param, int):
        return dict(epsg=request.param)
    return dict(crs=request.param)

@pytest.fixture(
    params=[
        26918,
        "epsg:26918",
        pytest.param(
            {"init": "epsg:26918", "no_defs": True},
            marks=pytest.mark.skipif(PYPROJ_LT_231, reason="segfault"),
        ),
        "+proj=utm +zone=18 +ellps=GRS80 +datum=NAD83 +units=m +no_defs ",
        {"proj": "utm", "zone": 18, "datum": "NAD83", "units": "m", "no_defs": True},
    ],
    ids=["epsg_number", "epsg_string", "epsg_dict", "proj4_string", "proj4_dict"],
)
def epsg26918(request):
    if isinstance(request.param, int):
        return dict(epsg=request.param)
    return dict(crs=request.param)

@pytest.mark.filterwarnings("ignore:'\\+init:DeprecationWarning")
@pytest.mark.filterwarnings("ignore:'\\+init:FutureWarning")
def test_transform2(epsg4326, epsg26918):
    # with PROJ >= 7, the transformation using EPSG code vs proj4 string is
    # slightly different due to use of grid files or not -> turn off network
    # to not use grid files at all for this test
    os.environ["PROJ_NETWORK"] = "OFF"
    df = df_epsg26918()
    lonlat = df.to_crs(**epsg4326)
    utm = lonlat.to_crs(**epsg26918)
    # can't check for CRS equality, as the formats differ although representing
    # the same CRS
    assert_geodataframe_equal(df, utm, check_less_precise=True, check_crs=False)

def test_crs_axis_order__always_xy():
    df = GeoDataFrame(geometry=[Point(-1683723, 6689139)], crs="epsg:26918")
    lonlat = df.to_crs("epsg:4326")
    test_lonlat = GeoDataFrame(
        geometry=[Point(-110.1399901, 55.1350011)], crs="epsg:4326"
    )
    assert_geodataframe_equal(lonlat, test_lonlat, check_less_precise=True)

def test_skip_exact_same():
    df = df_epsg26918()
    utm = df.to_crs(df.crs)
    assert_geodataframe_equal(df, utm, check_less_precise=True)

# Test CRS on GeometryArray level
class TestGeometryArrayCRS:
    def setup_method(self):
        self.osgb = pyproj.CRS(27700)
        self.wgs = pyproj.CRS(4326)

        self.geoms = [Point(0, 0), Point(1, 1)]
        self.polys = [
            Polygon([(random.random(), random.random()) for i in range(3)])
            for _ in range(10)
        ]
        self.arr = from_shapely(self.polys, crs=27700)

    def test_array(self):
        arr = from_shapely(self.geoms)
        arr.crs = 27700
        assert arr.crs == self.osgb

        arr = from_shapely(self.geoms, crs=27700)
        assert arr.crs == self.osgb

        arr = GeometryArray(arr)
        assert arr.crs == self.osgb

        arr = GeometryArray(arr, crs=4326)
        assert arr.crs == self.wgs

    def test_series(self):
        s = GeoSeries(crs=27700)
        assert s.crs == self.osgb
        assert s.values.crs == self.osgb

        arr = from_shapely(self.geoms)
        s = GeoSeries(arr, crs=27700)
        assert s.crs == self.osgb
        assert s.values.crs == self.osgb

        # manually change CRS
        s.crs = 4326
        assert s.crs == self.wgs
        assert s.values.crs == self.wgs

        s = GeoSeries(self.geoms, crs=27700)
        assert s.crs == self.osgb
        assert s.values.crs == self.osgb

        arr = from_shapely(self.geoms, crs=27700)
        s = GeoSeries(arr)
        assert s.crs == self.osgb
        assert s.values.crs == self.osgb

        with pytest.warns(FutureWarning):
            s = GeoSeries(arr, crs=4326)
        assert s.crs == self.osgb

    @pytest.mark.filterwarnings("ignore:Assigning CRS")
    def test_dataframe(self):
        arr = from_shapely(self.geoms, crs=27700)
        df = GeoDataFrame(geometry=arr)
        assert df.crs == self.osgb
        assert df.geometry.crs == self.osgb
        assert df.geometry.values.crs == self.osgb

        arr = from_shapely(self.geoms)
        s = GeoSeries(arr, crs=27700)
        df = GeoDataFrame(geometry=s)
        assert df.crs == self.osgb
        assert df.geometry.crs == self.osgb
        assert df.geometry.values.crs == self.osgb

        # different passed CRS than array CRS is ignored
        with pytest.warns(FutureWarning, match="CRS mismatch"):
            df = GeoDataFrame(geometry=s, crs=4326)
        assert df.crs == self.osgb
        assert df.geometry.crs == self.osgb
        assert df.geometry.values.crs == self.osgb
        with pytest.warns(FutureWarning, match="CRS mismatch"):
            GeoDataFrame(geometry=s, crs=4326)
        with pytest.warns(FutureWarning, match="CRS mismatch"):
            GeoDataFrame({"data": [1, 2], "geometry": s}, crs=4326)
        with pytest.warns(FutureWarning, match="CRS mismatch"):
            GeoDataFrame(df, crs=4326).crs

        # manually change CRS
        arr = from_shapely(self.geoms)
        s = GeoSeries(arr, crs=27700)
        df = GeoDataFrame(geometry=s)
        df.crs = 4326
        assert df.crs == self.wgs
        assert df.geometry.crs == self.wgs
        assert df.geometry.values.crs == self.wgs

        df = GeoDataFrame(self.geoms, columns=["geom"], crs=27700)
        assert df.crs == self.osgb
        df = df.set_geometry("geom")
        assert df.crs == self.osgb
        assert df.geometry.crs == self.osgb
        assert df.geometry.values.crs == self.osgb
        assert df.geom.crs == self.osgb
        assert df.geom.values.crs == self.osgb

        df = GeoDataFrame(geometry=self.geoms, crs=27700)
        assert df.crs == self.osgb
        assert df.geometry.crs == self.osgb
        assert df.geometry.values.crs == self.osgb

        df = GeoDataFrame(crs=27700)
        df = df.set_geometry(self.geoms)
        assert df.crs == self.osgb
        assert df.geometry.crs == self.osgb
        assert df.geometry.values.crs == self.osgb

        # new geometry with set CRS has priority over GDF CRS
        df = GeoDataFrame(crs=27700)
        df = df.set_geometry(self.geoms, crs=4326)
        assert df.crs == self.wgs
        assert df.geometry.crs == self.wgs
        assert df.geometry.values.crs == self.wgs

        arr = from_shapely(self.geoms)
        s = GeoSeries(arr, crs=27700)
        df = GeoDataFrame()
        df = df.set_geometry(s)
        assert df.crs == self.osgb
        assert df.geometry.crs == self.osgb
        assert df.geometry.values.crs == self.osgb

        arr = from_shapely(self.geoms, crs=27700)
        df = GeoDataFrame()
        df = df.set_geometry(arr)
        assert df.crs == self.osgb
        assert df.geometry.crs == self.osgb
        assert df.geometry.values.crs == self.osgb

        arr = from_shapely(self.geoms)
        df = GeoDataFrame({"col1": [1, 2], "geometry": arr}, crs=4326)
        assert df.crs == self.wgs
        assert df.geometry.crs == self.wgs
        assert df.geometry.values.crs == self.wgs

        arr = from_shapely(self.geoms, crs=4326)
        df = GeoDataFrame({"col1": [1, 2], "geometry": arr})
        assert df.crs == self.wgs
        assert df.geometry.crs == self.wgs
        assert df.geometry.values.crs == self.wgs

        # geometry column without geometry
        df = GeoDataFrame({"geometry": [0, 1]})
        df.crs = 27700
        assert df.crs == self.osgb

    def test_dataframe_setitem(self):
        # new geometry CRS has priority over GDF CRS
        arr = from_shapely(self.geoms)
        s = GeoSeries(arr, crs=27700)
        df = GeoDataFrame()
        df["geometry"] = s
        assert df.crs == self.osgb
        assert df.geometry.crs == self.osgb
        assert df.geometry.values.crs == self.osgb

        arr = from_shapely(self.geoms, crs=27700)
        df = GeoDataFrame()
        df["geometry"] = arr
        assert df.crs == self.osgb
        assert df.geometry.crs == self.osgb
        assert df.geometry.values.crs == self.osgb

        # test to_crs case (GH1960)
        arr = from_shapely(self.geoms)
        df = GeoDataFrame({"col1": [1, 2], "geometry": arr}, crs=4326)
        df["geometry"] = df["geometry"].to_crs(27700)
        assert df.crs == self.osgb
        assert df.geometry.crs == self.osgb
        assert df.geometry.values.crs == self.osgb

        # test changing geometry crs not in the geometry column doesn't change the crs
        arr = from_shapely(self.geoms)
        df = GeoDataFrame(
            {"col1": [1, 2], "geometry": arr, "other_geom": arr}, crs=4326
        )
        df["other_geom"] = from_shapely(self.geoms, crs=27700)
        assert df.crs == self.wgs
        assert df.geometry.crs == self.wgs
        assert df["geometry"].crs == self.wgs
        assert df["other_geom"].crs == self.osgb

    @pytest.mark.parametrize(
        "scalar", [None, Point(0, 0), LineString([(0, 0), (1, 1)])]
    )
    def test_scalar(self, scalar):
        with pytest.warns(FutureWarning):
            df = GeoDataFrame()
            df.crs = 4326
        df["geometry"] = scalar
        assert df.crs == self.wgs
        assert df.geometry.crs == self.wgs
        assert df.geometry.values.crs == self.wgs

    def test_read_file(self):
        nybb_filename = datasets.get_path("nybb")
        df = read_file(nybb_filename)
        assert df.crs == pyproj.CRS(2263)
        assert df.geometry.crs == pyproj.CRS(2263)
        assert df.geometry.values.crs == pyproj.CRS(2263)

    def test_multiple_geoms(self):
        arr = from_shapely(self.geoms, crs=27700)
        s = GeoSeries(self.geoms, crs=4326)
        df = GeoDataFrame(s, geometry=arr, columns=["col1"])
        assert df.crs == self.osgb
        assert df.geometry.crs == self.osgb
        assert df.geometry.values.crs == self.osgb
        assert df.col1.crs == self.wgs
        assert df.col1.values.crs == self.wgs

    def test_multiple_geoms_set_geom(self):
        arr = from_shapely(self.geoms, crs=27700)
        s = GeoSeries(self.geoms, crs=4326)
        df = GeoDataFrame(s, geometry=arr, columns=["col1"])
        df = df.set_geometry("col1")
        assert df.crs == self.wgs
        assert df.geometry.crs == self.wgs
        assert df.geometry.values.crs == self.wgs
        assert df["geometry"].crs == self.osgb
        assert df["geometry"].values.crs == self.osgb

    def test_assign_cols(self):
        arr = from_shapely(self.geoms, crs=27700)
        s = GeoSeries(self.geoms, crs=4326)
        df = GeoDataFrame(s, geometry=arr, columns=["col1"])
        df["geom2"] = s
        df["geom3"] = s.values
        df["geom4"] = from_shapely(self.geoms)
        assert df.crs == self.osgb
        assert df.geometry.crs == self.osgb
        assert df.geometry.values.crs == self.osgb
        assert df.geom2.crs == self.wgs
        assert df.geom2.values.crs == self.wgs
        assert df.geom3.crs == self.wgs
        assert df.geom3.values.crs == self.wgs
        assert df.geom4.crs is None
        assert df.geom4.values.crs is None

    def test_copy(self):
        arr = from_shapely(self.geoms, crs=27700)
        s = GeoSeries(self.geoms, crs=4326)
        df = GeoDataFrame(s, geometry=arr, columns=["col1"])

        arr_copy = arr.copy()
        assert arr_copy.crs == arr.crs

        s_copy = s.copy()
        assert s_copy.crs == s.crs
        assert s_copy.values.crs == s.values.crs

        df_copy = df.copy()
        assert df_copy.crs == df.crs
        assert df_copy.geometry.crs == df.geometry.crs
        assert df_copy.geometry.values.crs == df.geometry.values.crs
        assert df_copy.col1.crs == df.col1.crs
        assert df_copy.col1.values.crs == df.col1.values.crs

    def test_rename(self):
        arr = from_shapely(self.geoms, crs=27700)
        s = GeoSeries(self.geoms, crs=4326)
        df = GeoDataFrame(s, geometry=arr, columns=["col1"])
        df = df.rename(columns={"geometry": "geom"}).set_geometry("geom")
        assert df.crs == self.osgb
        assert df.geometry.crs == self.osgb
        assert df.geometry.values.crs == self.osgb

        df = df.rename_geometry("geom2")
        assert df.crs == self.osgb
        assert df.geometry.crs == self.osgb
        assert df.geometry.values.crs == self.osgb

        df = df.rename(columns={"col1": "column1"})
        assert df.column1.crs == self.wgs
        assert df.column1.values.crs == self.wgs

    def test_geoseries_to_crs(self):
        s = GeoSeries(self.geoms, crs=27700)
        s = s.to_crs(4326)
        assert s.crs == self.wgs
        assert s.values.crs == self.wgs

        df = GeoDataFrame(geometry=s)
        assert df.crs == self.wgs
        df = df.to_crs(27700)
        assert df.crs == self.osgb
        assert df.geometry.crs == self.osgb
        assert df.geometry.values.crs == self.osgb

        # make sure that only active geometry is transformed
        arr = from_shapely(self.geoms, crs=4326)
        df["col1"] = arr
        df = df.to_crs(3857)
        assert df.col1.crs == self.wgs
        assert df.col1.values.crs == self.wgs

    def test_array_to_crs(self):
        arr = from_shapely(self.geoms, crs=27700)
        arr = arr.to_crs(4326)
        assert arr.crs == self.wgs

    def test_from_shapely(self):
        arr = from_shapely(self.geoms, crs=27700)
        assert arr.crs == self.osgb

    def test_from_wkb(self):
        L_wkb = [p.wkb for p in self.geoms]
        arr = from_wkb(L_wkb, crs=27700)
        assert arr.crs == self.osgb

    def test_from_wkt(self):
        L_wkt = [p.wkt for p in self.geoms]
        arr = from_wkt(L_wkt, crs=27700)
        assert arr.crs == self.osgb

    def test_points_from_xy(self):
        df = pd.DataFrame([{"x": x, "y": x, "z": x} for x in range(10)])
        arr = points_from_xy(df["x"], df["y"], crs=27700)
        assert arr.crs == self.osgb

    # setting CRS in GeoSeries should not set it in passed array without CRS
    def test_original(self):
        arr = from_shapely(self.geoms)
        s = GeoSeries(arr, crs=27700)
        assert arr.crs is None
        assert s.crs == self.osgb

    def test_ops(self):
        arr = self.arr
        bound = arr.boundary
        assert bound.crs == self.osgb

        cent = arr.centroid
        assert cent.crs == self.osgb

        hull = arr.convex_hull
        assert hull.crs == self.osgb

        envelope = arr.envelope
        assert envelope.crs == self.osgb

        exterior = arr.exterior
        assert exterior.crs == self.osgb

        representative_point = arr.representative_point()
        assert representative_point.crs == self.osgb

    def test_binary_ops(self):
        arr = self.arr
        quads = []
        while len(quads) < 10:
            geom = Polygon([(random.random(), random.random()) for i in range(4)])
            if geom.is_valid:
                quads.append(geom)

        arr2 = from_shapely(quads, crs=27700)

        difference = arr.difference(arr2)
        assert difference.crs == self.osgb

        intersection = arr.intersection(arr2)
        assert intersection.crs == self.osgb

        symmetric_difference = arr.symmetric_difference(arr2)
        assert symmetric_difference.crs == self.osgb

        union = arr.union(arr2)
        assert union.crs == self.osgb

    def test_other(self):
        arr = self.arr

        buffer = arr.buffer(5)
        assert buffer.crs == self.osgb

        interpolate = arr.exterior.interpolate(0.1)
        assert interpolate.crs == self.osgb

        simplify = arr.simplify(5)
        assert simplify.crs == self.osgb

    @pytest.mark.parametrize(
        "attr, arg",
        [
            ("affine_transform", ([0, 1, 1, 0, 0, 0],)),
            ("translate", ()),
            ("rotate", (10,)),
            ("scale", ()),
            ("skew", ()),
        ],
    )
    def test_affinity_methods(self, attr, arg):
        result = getattr(self.arr, attr)(*arg)

        assert result.crs == self.osgb

    def test_slice(self):
        s = GeoSeries(self.arr, crs=27700)
        assert s.iloc[1:].values.crs == self.osgb

        df = GeoDataFrame({"col1": self.arr}, geometry=s)
        assert df.iloc[1:].geometry.values.crs == self.osgb
        assert df.iloc[1:].col1.values.crs == self.osgb

    def test_concat(self):
        s = GeoSeries(self.arr, crs=27700)
        assert pd.concat([s, s]).values.crs == self.osgb

        df = GeoDataFrame({"col1": from_shapely(self.geoms, crs=4326)}, geometry=s)
        assert pd.concat([df, df]).geometry.values.crs == self.osgb
        assert pd.concat([df, df]).col1.values.crs == self.wgs

    def test_merge(self):
        arr = from_shapely(self.geoms, crs=27700)
        s = GeoSeries(self.geoms, crs=4326)
        df = GeoDataFrame({"col1": s}, geometry=arr)
        df2 = GeoDataFrame({"col2": s}, geometry=arr).rename_geometry("geom")
        merged = df.merge(df2, left_index=True, right_index=True)
        assert merged.col1.values.crs == self.wgs
        assert merged.geometry.values.crs == self.osgb
        assert merged.col2.values.crs == self.wgs
        assert merged.geom.values.crs == self.osgb
        assert merged.crs == self.osgb

    # CRS should be assigned to geometry
    def test_deprecation(self):
        with pytest.warns(FutureWarning):
            df = GeoDataFrame([], crs=27700)

        # https://github.com/geopandas/geopandas/issues/1548
        # ensure we still have converted the crs value to a CRS object
        assert isinstance(df.crs, pyproj.CRS)

        with pytest.warns(FutureWarning):
            df = GeoDataFrame([])
            df.crs = 27700

        assert isinstance(df.crs, pyproj.CRS)

    # make sure that geometry column from list has CRS (__setitem__)
    def test_setitem_geometry(self):
        arr = from_shapely(self.geoms, crs=27700)
        df = GeoDataFrame({"col1": [0, 1]}, geometry=arr)

        df["geometry"] = [g for g in df.geometry]
        assert df.geometry.values.crs == self.osgb

        df2 = GeoDataFrame({"col1": [0, 1]}, geometry=arr)
        df2["geometry"] = from_shapely(self.geoms, crs=4326)
        assert df2.geometry.values.crs == self.wgs

    def test_astype(self):
        arr = from_shapely(self.geoms, crs=27700)
        df = GeoDataFrame({"col1": [0, 1]}, geometry=arr)
        df2 = df.astype({"col1": str})
        assert df2.crs == self.osgb

    def test_apply(self):
        s = GeoSeries(self.arr)
        assert s.crs == 27700

        # apply preserves the CRS if the result is a GeoSeries
        result = s.apply(lambda x: x.centroid)
        assert result.crs == 27700

    def test_apply_geodataframe(self):
        df = GeoDataFrame({"col1": [0, 1]}, geometry=self.geoms, crs=27700)
        assert df.crs == 27700

        # apply preserves the CRS if the result is a GeoDataFrame
        result = df.apply(lambda col: col, axis=0)
        assert result.crs == 27700
        result = df.apply(lambda row: row, axis=1)
        assert result.crs == 27700

class TestSetCRS:
    @pytest.mark.parametrize(
        "constructor",
        [
            lambda geoms, crs: GeoSeries(geoms, crs=crs),
            lambda geoms, crs: GeoDataFrame(geometry=geoms, crs=crs),
        ],
        ids=["geoseries", "geodataframe"],
    )
    def test_set_crs(self, constructor):
        naive = constructor([Point(0, 0), Point(1, 1)], crs=None)
        assert naive.crs is None

        # by default returns a copy
        result = naive.set_crs(crs="EPSG:4326")
        assert result.crs == "EPSG:4326"
        assert naive.crs is None

        result = naive.set_crs(epsg=4326)
        assert result.crs == "EPSG:4326"
        assert naive.crs is None

        # with inplace=True
        result = naive.set_crs(crs="EPSG:4326", inplace=True)
        assert result is naive
        assert result.crs == naive.crs == "EPSG:4326"

        # raise for non-naive when crs would be overridden
        non_naive = constructor([Point(0, 0), Point(1, 1)], crs="EPSG:4326")
        assert non_naive.crs == "EPSG:4326"
        with pytest.raises(ValueError, match="already has a CRS"):
            non_naive.set_crs("EPSG:3857")

        # allow for equal crs
        result = non_naive.set_crs("EPSG:4326")
        assert result.crs == "EPSG:4326"

        # replace with allow_override=True
        result = non_naive.set_crs("EPSG:3857", allow_override=True)
        assert non_naive.crs == "EPSG:4326"
        assert result.crs == "EPSG:3857"

        result = non_naive.set_crs("EPSG:3857", allow_override=True, inplace=True)
        assert non_naive.crs == "EPSG:3857"
        assert result.crs == "EPSG:3857"

        # raise error when no crs is passed
        with pytest.raises(ValueError):
            naive.set_crs(crs=None, epsg=None)


import torch
import random
import numpy as np

def set_seed(seed):
    """ Use this to set ALL the random seeds to a fixed value and take out any
        randomness from cuda kernels
    """
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)

    #uses inbuilt cudnn auto-tuner to find the fastest convolution algorithms.
    torch.backends.cudnn.benchmark = False
    torch.backends.cudnn.enabled   = False

    return True

seed = 42
device = 'cpu'

if torch.cuda.device_count() > 0 and torch.cuda.is_available():
    print("Cuda installed! Running on GPU!")
    device = 'cuda'
else:
    print("No GPU available!")


import numpy as np
import pandas as pd

col_names = ['Title', 'Air date', 'Production code', 'Season', 'Number in season',
             'Number in series', 'US viewers (million)', 'Views', 'IMDB rating']

select_cols = ['Title','Air date','Production code','IMDB rating']

simpsons = pd.read_csv('simpsons-episodes.tsv',
                 sep='\t',
                 encoding='UTF-8',
                 names=col_names,
                 usecols=select_cols,
                 skiprows=4,
                 index_col='Production code',
                 na_values=['no_val'],
                 parse_dates=['Air date'])


import abc

import numpy as np

# This is an abstract class that specifies how a concrete implementation of a bandit algorithm should behave.
class BanditLearner(metaclass=abc.ABCMeta):
    # n_arms = number of arms the learner can pull.
    def __init__(self, n_arms: int):
        assert n_arms > 1
        self.n_arms = n_arms
        self.t = 0  # current round.
        self.rewards_per_arm = [[] for _ in range(n_arms)]  # List of lists (using python list), example rewards_per_arm[0] contains the list of pulled rewards from arm 0.
        self.collected_rewards = np.array([])  # Numpy array containing all rewards.

    # pulled_arm = arm pulled.
    # reward = reward of arm pulled.
    def update_observations(self, pulled_arm: int, reward: float) -> None:
        assert pulled_arm < self.n_arms
        self.rewards_per_arm[pulled_arm].append(reward)
        self.collected_rewards = np.append(self.collected_rewards, reward)

    @abc.abstractmethod
    def pull_arm(self) -> int:
        pass

    @abc.abstractmethod
    def update(self, pulled_arm: int, reward: float) -> None:
        pass


#!/usr/bin/env python
#
# Copyright (c) 2019 Opticks Team. All Rights Reserved.
#
# This file is part of Opticks
# (see https://bitbucket.org/simoncblyth/opticks).
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

"""
np.py
========

This is intended as a quick and dirty dumper/comparer of npy files,
for quick initial comparisons of small numbers of arrays.

When wishing to get specific to certain collections of npy files,
move to specific other scripts such as ab.py etc..

Keep this general.

NB when intended argument values begin with a negative sign
you have to handhold argparser to get them in::

   np.py GMergedMesh/0/nodeinfo.npy -viF --slice="-20:-1"

Because arguments beginning with negative signs are very common with slices,
underscores in arguments are converted to "-" signs for the slice arguments,
allowing::

   np.py GMergedMesh/0/nodeinfo.npy -viF -s _20:_1

::

   ipython -i $(which np.py) -- tmp/blyth/OKG4Test/evt/g4live/natural/-1/so.npy source/evt/g4live/natural/-1/so.npy -v

   inp(){ ipython -i $(which np.py) -- $* ; }

   inp tmp/blyth/OKG4Test/evt/g4live/natural/-1/so.npy source/evt/g4live/natural/-1/so.npy -v

Comparing directories containing npy::

   np.py tmp/blyth/OKG4Test/evt/g4live/natural/{-1,1}

Even more minimal::

   python -c "import numpy as np, sys ; np.set_printoptions(suppress=True) ; print np.load(sys.argv[1]) "

"""
import sys, fnmatch, os, logging, numpy as np, commands, argparse
from collections import OrderedDict as odict

log = logging.getLogger(__name__)

is_npy_ = lambda _:fnmatch.fnmatch(_,"*.npy")
is_txt_ = lambda _:fnmatch.fnmatch(_,"*.txt")
is_dir_ = lambda _:os.path.isdir(_)

from opticks.bin.md5 import digest_
from opticks.ana.base import stamp_

def dump_one(a, args):
    print(a.shape)
    if args.float:
        print("f32")
        print(a[args.slice].view(np.float32))
    pass
    if args.int:
        print("i32")
        print(a[args.slice].view(np.int32))
    pass

def dump_tree(base, args):
    """
    Recursively lists shapes of all .npy files
    and line lengths of .txt files.
    """
    print(os.path.abspath(base))
    for root, dirs, files in os.walk(base):
        sfiles = sorted(files)
        if args.txt:
            for name in filter(is_txt_,sfiles):
                path = os.path.join(root, name)
                txt_brief(path)
            pass
        pass
        for name in filter(is_npy_,sfiles):
            path = os.path.join(root, name)
            npy_brief(path, args)
        pass
    pass

def txt_brief( path, label="." ):
    fdig = digest_(path)
    stmp = stamp_(path)
    lines = len(file(path, "r").readlines())
    print("%s : %60s : %20s : %s : %s " % ( label, path, lines , fdig, stmp ))

def npy_brief( path, args, label="."):
    a = np.load(path)
    fdig = digest_(path)
    stmp = stamp_(path)
    print("%s : %60s : %20s : %s : %s " % ( label, path, repr(a.shape), fdig, stmp ))
    if args.verbose > 0:
        dump_one(a, args)
    pass
    return a

def compare(a, b):
    df = a - b
    print( " max(a-b) %10.3g  min(a-b) %10.3g " % (np.max(df), np.min(df)) )

if __name__ == '__main__':
    logging.basicConfig(level=logging.INFO)

    parser = argparse.ArgumentParser(__doc__)
    parser.add_argument(  "paths", nargs='*', help="File or directory paths with .npy" )
    parser.add_argument("-v","--verbose", action="store_true", default=False )
    parser.add_argument("-f","--float", action="store_true", default=True )
    parser.add_argument("-F","--nofloat", dest="float", action="store_false" )
    parser.add_argument("-T","--notxt", dest="txt", action="store_false", default=True )
    parser.add_argument("-i","--int", action="store_true", default=False )
    parser.add_argument("-n","--threshold", type=int, default=1000 )
    parser.add_argument("-d","--debug", action="store_true", default=False )
    parser.add_argument("-s","--slice", default=None )

    args = parser.parse_args()
    if args.debug:
        print(args)
    pass
    if args.slice is not None:
        args.slice = slice( *map(int,args.slice.replace("_","-").split(":")) )
    pass
    if args.debug:
        print("args.slice %r " % args.slice)
    pass

    np.set_printoptions(suppress=True, precision=4, linewidth=200, threshold=int(args.threshold))

    dirs = filter(is_dir_, args.paths)
    npys = filter(is_npy_, args.paths)
    if args.debug:
        print("dirs:%s" % repr(dirs))
        print("npys:%s" % repr(npys))
    pass

    if len(dirs) > 0:
        for p in dirs:
            dump_tree(p, args)
        pass
    elif len(dirs) == 0 and len(npys) == 0:
        dump_tree(".", args)
    else:
        pass
    pass

    if len(npys) > 0:
        n = odict()
        labels = "abcdefghijklmnopqrstuvwxyz"
        labels += labels.upper()
        for i,path in enumerate(npys):
            n[i] = npy_brief(path, args, label=labels[i])
        pass
        ln = len(npys)

        if ln > 0: a=n[0]
        if ln > 1: b=n[1]
        if ln > 2: c=n[2]
        if ln > 3: d=n[3]

        if ln == 2:
            compare(a,b)
        pass
    pass


import numpy as np
import os
a = np.arange(20)
np.save('temp_arra.npy', a)
print("Check if 'temp_arra.npy' exists or not?")
if os.path.exists('temp_arra.npy'):
    x2 = np.load('temp_arra.npy')
    print(np.array_equal(a, x2))


# encoding=utf8

"""Algorithm test case module."""

import logging
from queue import Queue
from threading import Thread
from unittest import TestCase

import numpy as np
from numpy import random as rnd

from WeOptPy.util import objects2array
from WeOptPy.task.interfaces import (
	Task,
	UtilityFunction
)
from WeOptPy.task import StoppingTask
from WeOptPy.algorithms.interfaces import (
	Algorithm,
	Individual
)

logging.basicConfig()
logger = logging.getLogger('WeOptPy.test')
logger.setLevel('INFO')

__all__ = [
	'Sphere',
	'AlgorithmTestCase'
]

class Sphere(UtilityFunction):
	r"""Testing benchmark class.

	Date:
		April 2019

	Author:
		Klemen Berkovič

	See Also:
		* :class:`WeOptPy.task.interfaces.UtilityFunction`
	"""
	def __init__(self):
		UtilityFunction.__init__(self, -5.12, 5.12)

	def function(self):
		return lambda x: np.sum(x ** 2)

class IndividualTestCase(TestCase):
	r"""Test case for testing Individual class.

	Date:
		April 2019

	Author:
		Klemen Berkovič

	See Also:
		* :class:`WeOptPy.algorithms.interfaces.Individual`
	"""
	def setUp(self):
		self.D = 20
		self.x, self.task = rnd.uniform(-100, 100, self.D), StoppingTask(d=self.D, no_fes=230, no_gen=np.inf, benchmark=Sphere())
		self.s1, self.s2, self.s3 = Individual(x=self.x, e=False), Individual(task=self.task, rand=rnd), Individual(task=self.task)

	def test_generate_solution_fine(self):
		self.assertTrue(self.task.is_feasible(self.s2))
		self.assertTrue(self.task.is_feasible(self.s3))

	def test_evaluate_fine(self):
		self.s1.evaluate(self.task)
		self.assertAlmostEqual(self.s1.f, self.task.eval(self.x))

	def test_repair_fine(self):
		s = Individual(x=np.full(self.D, 100))
		self.assertFalse(self.task.is_feasible(s.x))

	def test_eq_fine(self):
		self.assertFalse(self.s1 == self.s2)
		self.assertTrue(self.s1 == self.s1)
		s = Individual(x=self.s1.x)
		self.assertTrue(s == self.s1)

	def test_str_fine(self):
		self.assertEqual(str(self.s1), '%s -> %s' % (self.x, np.inf))

	def test_getitem_fine(self):
		for i in range(self.D): self.assertEqual(self.s1[i], self.x[i])

	def test_len_fine(self):
		self.assertEqual(len(self.s1), len(self.x))

def init_pop_numpy(task, n, **kwargs):
	r"""Custom population initialization function for numpy individual type.

	Args:
		task (Task): Optimization task.
		n (int): Population size.
		kwargs (dict): Additional arguments.

	Returns:
		Tuple[numpy.ndarray, numpy.ndarray, list, dict):
			1. Initialized population.
			2. Initialized populations fitness/function values.
			3. Additional arguments.
			4. Additional keyword arguments.
	"""
	pop = np.full((n, task.D), 0.0)
	fpop = np.apply_along_axis(task.eval, 1, pop)
	return pop, fpop, [], {}

def init_pop_individual(task, n, itype, **kwargs):
	r"""Custom population initialization function for numpy individual type.

	Args:
		task (Task): Optimization task.
		n (int): Population size.
		itype (Individual): Type of individual in population.
		kwargs (Dict[str, Any]): Additional arguments.

	Returns:
		Tuple[numpy.ndarray, numpy.ndarray, list, dict):
			1. Initialized population.
			2. Initialized populations fitness/function values.
			3. Additional arguments.
			4. Additional keyword arguments.
	"""
	pop = objects2array([itype(x=np.full(task.D, 0.0), task=task) for _ in range(n)])
	return pop, np.asarray([x.f for x in pop]), [], {}

class AlgorithmBaseTestCase(TestCase):
	r"""Test case for testing Algorithm class.

	Date:
		April 2019

	Author:
		Klemen Berkovič

	Attributes:
		seed (int): Starting seed of random generator.
		rnd (mtrand.RandomState): Random generator.
		a (Algorithm): Algorithm to use for testing.

	See Also:
		* :class:`NiaPy.algorithms.Individual`
	"""
	def setUp(self):
		self.seed = 1
		self.rnd = rnd.RandomState(self.seed)
		self.a = Algorithm(seed=self.seed)

	def test_algorithm_info_fine(self):
		r"""Check if method works fine."""
		i = Algorithm.algorithm_info()
		self.assertIsNotNone(i)

	def test_algorithm_getParameters_fine(self):
		r"""Check if method works fine."""
		algo = Algorithm()
		params = algo.get_parameters()
		self.assertIsNotNone(params)

	def test_type_parameters_fine(self):
		d = Algorithm.type_parameters()
		self.assertIsNotNone(d)

	def test_init_population_numpy_fine(self):
		r"""Test if custome generation initialization works ok."""
		a = Algorithm(n=10, init_pop_func=init_pop_numpy)
		t = Task(d=20, benchmark=Sphere())
		self.assertTrue(np.array_equal(np.full((10, t.D), 0.0), a.init_population(t)[0]))

	def test_init_population_individual_fine(self):
		r"""Test if custom generation initialization works ok."""
		a = Algorithm(n=10, init_pop_func=init_pop_individual, itype=Individual)
		t = Task(d=20, benchmark=Sphere())
		i = Individual(x=np.full(t.D, 0.0), task=t)
		pop, fpop, args, kwargs = a.init_population(t)
		for e in pop: self.assertEqual(i, e)

	def test_set_parameters(self):
		self.a.set_parameters(t=None, a=20)
		self.assertRaises(AttributeError, lambda: self.assertEqual(self.a.a, None))

	def test_randint_fine(self):
		o = self.a.randint(maximum=20, minimum=10, d=[10, 10])
		self.assertEqual(o.shape, (10, 10))
		self.assertTrue(np.array_equal(self.rnd.randint(10, 20, (10, 10)), o))
		o = self.a.randint(maximum=20, minimum=10, d=(10, 5))
		self.assertEqual(o.shape, (10, 5))
		self.assertTrue(np.array_equal(self.rnd.randint(10, 20, (10, 5)), o))
		o = self.a.randint(maximum=20, minimum=10, d=10)
		self.assertEqual(o.shape, (10,))
		self.assertTrue(np.array_equal(self.rnd.randint(10, 20, 10), o))

	def test_randn_fine(self):
		a = self.a.randn([1, 2])
		self.assertEqual(a.shape, (1, 2))
		self.assertTrue(np.array_equal(self.rnd.randn(1, 2), a))
		a = self.a.randn(1)
		self.assertEqual(len(a), 1)
		self.assertTrue(np.array_equal(self.rnd.randn(1), a))
		a = self.a.randn(2)
		self.assertEqual(len(a), 2)
		self.assertTrue(np.array_equal(self.rnd.randn(2), a))
		a = self.a.randn()
		self.assertIsInstance(a, float)
		self.assertTrue(np.array_equal(self.rnd.randn(), a))

	def test_uniform_fine(self):
		a = self.a.uniform(-10, 10, [10, 10])
		self.assertEqual(a.shape, (10, 10))
		self.assertTrue(np.array_equal(self.rnd.uniform(-10, 10, (10, 10)), a))
		a = self.a.uniform(4, 10, (4, 10))
		self.assertEqual(len(a), 4)
		self.assertEqual(len(a[0]), 10)
		self.assertTrue(np.array_equal(self.rnd.uniform(4, 10, (4, 10)), a))
		a = self.a.uniform(1, 4, 2)
		self.assertEqual(len(a), 2)
		self.assertTrue(np.array_equal(self.rnd.uniform(1, 4, 2), a))
		a = self.a.uniform(10, 100)
		self.assertIsInstance(a, float)
		self.assertEqual(self.rnd.uniform(10, 100), a)

	def test_normal_fine(self):
		a = self.a.normal(-10, 10, [10, 10])
		self.assertEqual(a.shape, (10, 10))
		self.assertTrue(np.array_equal(self.rnd.normal(-10, 10, (10, 10)), a))
		a = self.a.normal(4, 10, (4, 10))
		self.assertEqual(len(a), 4)
		self.assertEqual(len(a[0]), 10)
		self.assertTrue(np.array_equal(self.rnd.normal(4, 10, (4, 10)), a))
		a = self.a.normal(1, 4, 2)
		self.assertEqual(len(a), 2)
		self.assertTrue(np.array_equal(self.rnd.normal(1, 4, 2), a))
		a = self.a.normal(10, 100)
		self.assertIsInstance(a, float)
		self.assertEqual(self.rnd.normal(10, 100), a)

class TestingTask(StoppingTask, TestCase):
	r"""Testing task.

	Date:
		April 2019

	Author:
		Klemen Berkovič

	See Also:
		* :class:`WeOptPy.task.StoppingTask`
	"""
	def names(self):
		r"""Get names of benchmark.

		Returns:
			List[str]: List of task names.
		"""
		return self.benchmark.Name

	def eval(self, x):
		r"""Check if is algorithm trying to evaluate solution out of bounds."""
		self.assertTrue(self.is_feasible(x), 'Solution %s is not in feasible space!!!' % x)
		return StoppingTask.eval(self, x)

class AlgorithmTestCase(TestCase):
	r"""Base class for testing other algorithms.

	Date:
		April 2019

	Author:
		Klemen Berkovič

	Attributes:
		D (List[int]): Dimension of problem.
		nGEN (int): Number of generations/iterations.
		nFES (int): Number of function evaluations.
		seed (int): Starting seed of random generator.

	See Also:
		* :class:`WeOptPy.algorithms.Algorithm`
	"""
	def setUp(self):
		r"""Setup basic parameters of the algorithm run."""
		self.D, self.nGEN, self.nFES, self.seed = [10, 25], 1000, 1000, 1
		self.algo = Algorithm

	def test_algorithm_type_parameters(self):
		r"""Test if type parametes for algorithm work fine."""
		tparams = self.algo.type_parameters()
		self.assertIsNotNone(tparams)

	def test_algorithm_info_fine(self):
		r"""Test if algorithm info works fine."""
		info = self.algo.algorithm_info()
		self.assertIsNotNone(info)

	def test_algorithm_get_parameters_fine(self):
		r"""Test if algorithms parameters values are fine."""
		params = self.algo().get_parameters()
		self.assertIsNotNone(params)

	def __set_up_task(self, d=10, bech=Sphere, nFES=None, nGEN=None, verbose=False):
		r"""Setup optimization tasks for testing.

		Args:
			d (int): Dimension of the problem.
			bech (UtilityFunction): Optimization problem to use.
			nFES (int): Number of fitness/objective function evaluations.
			nGEN (int): Number of generations.
			verbose (bool): Verbose output.

		Returns:
			Task: Testing task.
		"""
		return TestingTask(d=d, no_fes=self.nFES if nFES is None else nFES, no_gen=self.nGEN if nGEN is None else nGEN, benchmark=bech, verbose=verbose)

	def test_algorithm_run(self, a=None, benc=Sphere):
		r"""Run main testing of algorithm.

		Args:
			a (Algorithm): First instance of algorithm.
			benc (UtilityFunction): Benchmark to use for testing.
		"""
		if a is None: return False
		for D in self.D:
			no_fes, no_gen = D * 1000, D * 1000
			task = self.__set_up_task(D, benc, nFES=no_fes, nGEN=no_gen)
			x = a.run(task)
			self.assertFalse(a.bad_run(), "Something went wrong at runtime of the algorithm --> %s" % a.exception)
			self.assertIsNotNone(x)
			logger.info('%s\n%s -> %s' % (task.names(), x[0], x[1]))
			self.assertAlmostEqual(task.benchmark.function()(x[0].x if isinstance(x[0], Individual) else x[0]), x[1], msg='Best individual fitness values does not mach the given one')
			self.assertAlmostEqual(task.x_f, x[1], msg='While running the algorithm, algorithm got better individual with fitness: %s' % task.x_f)
			self.assertTrue(no_fes >= task.Evals, msg='nfes: %d < evals: %d' % (no_fes, task.Evals))
			self.assertTrue(no_gen >= task.Iters, msg='ngen: %d < iters: %d' % (no_gen, task.Iters))
		return True

	def test_algorithm_run_parallel(self, a=None, b=None, benc=Sphere):
		r"""Run main testing of algorithm in parallel.

		Args:
			a (Algorithm): First instance of algorithm.
			b (Algorithm): Second instance of algorithm.
			benc (UtilityFunction): Benchmark to use for testing.
		"""
		if a is None or b is None: return False
		for D in self.D:
			no_fes, no_gen = D * 1000, D * 1000
			task1, task2 = self.__set_up_task(D, benc, nFES=no_fes, nGEN=no_gen), self.__set_up_task(D, benc, nFES=no_fes, nGEN=no_gen)
			q = Queue(maxsize=2)
			thread1, thread2 = Thread(target=lambda a, t, q: q.put(a.run(t)), args=(a, task1, q)), Thread(target=lambda a, t, q: q.put(a.run(t)), args=(b, task2, q))
			thread1.start(), thread2.start()
			thread1.join(), thread2.join()
			x, y = q.get(block=True), q.get(block=True)
			self.assertFalse(a.bad_run() or b.bad_run(), "Something went wrong at runtime of the algorithm --> %s" % a.exception)
			self.assertIsNotNone(x), self.assertIsNotNone(y)
			logger.info('%s\n%s -> %s\n%s -> %s' % (task1.names(), x[0], x[1], y[0], y[1]))
			self.assertAlmostEqual(task1.benchmark.function()(x[0].x if isinstance(x[0], Individual) else x[0]), x[1], msg='Best individual fitness values does not mach the given one')
			self.assertAlmostEqual(task1.x_f, x[1], msg='While running the algorithm, algorithm got better individual with fitness: %s' % task1.x_f)
			self.assertTrue(np.array_equal(x[0], y[0]), 'Results can not be reproduced, check usages of random number generator')
			self.assertAlmostEqual(x[1], y[1], msg='Results can not be reproduced or bad function value')
			self.assertTrue(no_fes >= task1.Evals, msg='nfes: %s < evals: %s' % (no_fes, task1.Evals)), self.assertEqual(task1.Evals, task2.Evals, msg='task1: %d != task2: %d' % (task1.Evals, task2.Evals))
			self.assertTrue(no_gen >= task1.Iters, msg='ngen: %s < iters: %s' % (no_gen, task1.Iters)), self.assertEqual(task1.Iters, task2.Iters, msg='task1: %d != task2: %d' % (task1.Iters, task2.Iters))
		return True

# vim: tabstop=3 noexpandtab shiftwidth=3 softtabstop=3


import numpy as np
import pandas as pd

a = np.arange(4)
print(a)
# [0 1 2 3]

s = pd.Series(a)
print(s)
# 0    0
# 1    1
# 2    2
# 3    3
# dtype: int64

index = ['A', 'B', 'C', 'D']
name = 'sample'
s = pd.Series(data=a, index=index, name=name, dtype='float')
print(s)
# A    0.0
# B    1.0
# C    2.0
# D    3.0
# Name: sample, dtype: float64

a = np.arange(12).reshape((4, 3))
print(a)
# [[ 0  1  2]
#  [ 3  4  5]
#  [ 6  7  8]
#  [ 9 10 11]]

# s = pd.Series(a)
# print(s)
# Exception: Data must be 1-dimensional

s = pd.Series(a[2])
print(s)
# 0    6
# 1    7
# 2    8
# dtype: int64

s = pd.Series(a.T[2])
print(s)
# 0     2
# 1     5
# 2     8
# 3    11
# dtype: int64

a = np.arange(12).reshape((4, 3))
print(a)
# [[ 0  1  2]
#  [ 3  4  5]
#  [ 6  7  8]
#  [ 9 10 11]]

df = pd.DataFrame(a)
print(df)
#    0   1   2
# 0  0   1   2
# 1  3   4   5
# 2  6   7   8
# 3  9  10  11

index = ['A', 'B', 'C', 'D']
columns = ['a', 'b', 'c']
df = pd.DataFrame(data=a, index=index, columns=columns, dtype='float')
print(df)
#      a     b     c
# A  0.0   1.0   2.0
# B  3.0   4.0   5.0
# C  6.0   7.0   8.0
# D  9.0  10.0  11.0


"""
Custom Module Definition
========================

**Author**: Yi-Hsiang Lai (seanlatias@github.com)

In this tutorial, we will introduce a new API called ``module``, which allows
users to define a hardware module.
"""

import heterocl as hcl
import numpy as np

##############################################################################
# Defining a Hardware Module
# --------------------------
# It is important for users to define a hardware module. The main reason is
# that by reusing the defined hardware module, we can reduce the resource
# usage of the design. To define a module, what we need to do is to define a
# Python function. Then, apply the function with a decorator. Within the
# decorator, we need to specify the shapes of the arguments. Following we show
# an example of defining a hardware module that return the maximum value of
# two tensors with a given index.
#
# Note that in this example, we have three input arguments, which are `A`, `B`,
# and `x`. The first two arguments are tensors with shape `(10,)` while the
# last argument is a variable. To represent the shape of a variable, we use an
# empty tuple `()`.
#
# Another thing to be noted is that we use ``hcl.return_`` for the return
# value. We can see that we can have multiple `return` statements.
#
# Use the Defined Module
# ----------------------
# To use the module, it is just like a normal Python call. There is nothing
# special here. Following we show an example of finding the element-wise
# maximum value of four tensors.

hcl.init()

def maximum(A, B, C, D):

    @hcl.def_([A.shape, B.shape, ()])
    def find_max(A, B, x):
        with hcl.if_(A[x] > B[x]):
            hcl.return_(A[x])
        with hcl.else_():
            hcl.return_(B[x])

    max_1 = hcl.compute(A.shape, lambda x: find_max(A, B, x), "max_1")
    max_2 = hcl.compute(A.shape, lambda x: find_max(C, D, x), "max_2")
    return hcl.compute(A.shape, lambda x: find_max(max_1, max_2, x), "max_o")

##############################################################################
# We can first inspect the generated IR. You can see that for each computation,
# we reuse the same module to find the maximum.

A = hcl.placeholder((10,), "A")
B = hcl.placeholder((10,), "B")
C = hcl.placeholder((10,), "C")
D = hcl.placeholder((10,), "D")

s = hcl.create_schedule([A, B, C, D], maximum)
print(hcl.lower(s))

##############################################################################
# Finally, let's run the algorithm and check the results

f = hcl.build(s)

a = np.random.randint(100, size=(10,))
b = np.random.randint(100, size=(10,))
c = np.random.randint(100, size=(10,))
d = np.random.randint(100, size=(10,))
o = np.zeros(10)

hcl_A = hcl.asarray(a)
hcl_B = hcl.asarray(b)
hcl_C = hcl.asarray(c)
hcl_D = hcl.asarray(d)
hcl_O = hcl.asarray(o, dtype=hcl.Int())

f(hcl_A, hcl_B, hcl_C, hcl_D, hcl_O)

print("Input tensors:")
print(hcl_A)
print(hcl_B)
print(hcl_C)
print(hcl_D)
print("Output tensor:")
print(hcl_O)

# Test the correctness
m1 = np.maximum(a, b)
m2 = np.maximum(c, d)
m = np.maximum(m1, m2)
assert np.array_equal(hcl_O.asnumpy(), m)

##############################################################################
# Modules Without Return Statement
# --------------------------------
# HeteroCL also allows users to define modules without a return statement. The
# usage is exactly the same as what we just introduced. The only differece is
# that the module can be called in a stand-alone way. Namely, it does not need
# to be contained in any HeteroCL APIs. Let's use the same example of finding
# the maximum. However, this time we update the output directly.

hcl.init()

def maximum2(A, B, C, D):

    # B will be the tensor that holds the maximum values
    @hcl.def_([A.shape, B.shape])
    def find_max(A, B):
        with hcl.for_(0, A.shape[0]) as i:
            with hcl.if_(A[i] > B[i]):
                B[i] = A[i]

    find_max(A, B)
    find_max(C, D)
    find_max(B, D)

s = hcl.create_schedule([A, B, C, D], maximum2)
f = hcl.build(s)

##############################################################################
# In the above example, we can see that now without the return value, we can
# directly call the defined module. Let's check the results. They should be
# the same as our first example.

f(hcl_A, hcl_B, hcl_C, hcl_D)

print("Output tensor:")
print(hcl_D)

# Test the correctness
m1 = np.maximum(a, b)
m2 = np.maximum(c, d)
m = np.maximum(m1, m2)
assert np.array_equal(hcl_D.asnumpy(), m)

##############################################################################
# Data Type Customization for Modules
# -----------------------------------
# We can also apply data type customization to our defined modules. There are
# two ways to do that. First, you can specify the data types directly in the
# module decorator. Second, you can use the ``quantize`` and ``downsize`` APIs.
# Let's show how we can downsize the first example.

A = hcl.placeholder((10,), dtype=hcl.UInt(4))
B = hcl.placeholder((10,), dtype=hcl.UInt(4))
C = hcl.placeholder((10,), dtype=hcl.UInt(4))
D = hcl.placeholder((10,), dtype=hcl.UInt(4))

s = hcl.create_scheme([A, B, C, D], maximum)
# Downsize the input arguments and also the return value
s.downsize([maximum.find_max.A, maximum.find_max.B, maximum.find_max], hcl.UInt(4))
# We also need to downsize the intermediate results
s.downsize([maximum.max_1, maximum.max_2], hcl.UInt(4))
s = hcl.create_schedule_from_scheme(s)
f = hcl.build(s)

##############################################################################
# Let's run it.

hcl_A = hcl.asarray(a, hcl.UInt(4))
hcl_B = hcl.asarray(b, hcl.UInt(4))
hcl_C = hcl.asarray(c, hcl.UInt(4))
hcl_D = hcl.asarray(d, hcl.UInt(4))
hcl_O = hcl.asarray(o)

f(hcl_A, hcl_B, hcl_C, hcl_D, hcl_O)

print("Downsized output tensor:")
print(hcl_O)

##############################################################################
# We can see that the results are downsized to 4-bit numbers. We can double
# check this.

# Test the correctness
m1 = np.maximum(a%16, b%16)
m2 = np.maximum(c%16, d%16)
m = np.maximum(m1%16, m2%16)
assert np.array_equal(hcl_O.asnumpy(), m)


import numpy as np
import matplotlib.pyplot as plt

def mgc_plot(x, y, sim_name):
    """Plot sim and MGC-plot"""
    # simulation
    plt.figure(figsize=(8, 8))
    ax = plt.gca()
    ax.set_title(sim_name + " Simulation", fontsize=20)
    ax.scatter(x, y)
    ax.set_xlabel('X', fontsize=15)
    ax.set_ylabel('Y', fontsize=15)
    ax.axis('equal')
    ax.tick_params(axis="x", labelsize=15)
    ax.tick_params(axis="y", labelsize=15)
    plt.show()

np.random.seed(12345678)
x = np.linspace(-1, 1, num=100)
y = x + 0.3 * np.random.random(x.size)

mgc_plot(x, y, "Linear")


import unittest

from jax import numpy as jnp
from jax import random as rand

from cv.unsupervised import BMM

class BMMTests(unittest.TestCase):

    def test_k_should_be_greater_0(self):
        with self.assertRaises(AssertionError):
            BMM(-1)
        with self.assertRaises(AssertionError):
            BMM(0)

    def test_random_state_should_be_non_negative(self):
        with self.assertRaises(AssertionError):
            BMM(1, random_state=-1)

    def test_n_iteration_should_be_greater_0(self):
        with self.assertRaises(AssertionError):
            BMM(1, n_iter=0)
        with self.assertRaises(AssertionError):
            BMM(1, n_iter=-1)

    def test_eps_should_be_greater_0(self):
        with self.assertRaises(AssertionError):
            BMM(1, eps=-0.1)

    def test_eps_should_be_less_1(self):
        with self.assertRaises(AssertionError):
            BMM(1, eps=1.1)

    def test_E_step(self):
        X = jnp.array([[1], [0], [1]])
        dist_params = jnp.array([[0.7], [0.2]])
        p_mixture = jnp.array([0.66, 1-0.66])

        bmm = BMM(2)
        p_clusters = bmm._E_step(dist_params, p_mixture, X) # protected method, but this is Python:)))

        t1 = 0.66 * 0.7
        t2 = 0.66 * 0.3
        t3 = (1-0.66) * 0.2
        t4 = (1-0.66) * 0.8
        p_clusters_expected = jnp.array([[t1 / (t1 + t3), t2 / (t2 + t4), t1 / (t1 + t3)],
                                         [t3 / (t1 + t3), t4 / (t2 + t4), t3 / (t1 + t3)]])

        self.assertTrue(jnp.allclose(p_clusters_expected, p_clusters))

    def test_E_step_eps_cliping(self):
        X = jnp.array([[1], [0], [1]])
        dist_params = jnp.array([[0.96], [0.01]]) # dist params should be cliping to [0.8, 0.2]
        p_mixture = jnp.array([0.66, 1-0.66])

        bmm = BMM(2, eps=0.2)
        p_clusters = bmm._E_step(dist_params, p_mixture, X)

        t1 = 0.66 * 0.8
        t2 = 0.66 * 0.2
        t3 = (1-0.66) * 0.2
        t4 = (1-0.66) * 0.8
        p_clusters_expected = jnp.array([[t1 / (t1 + t3), t2 / (t2 + t4), t1 / (t1 + t3)],
                                         [t3 / (t1 + t3), t4 / (t2 + t4), t3 / (t1 + t3)]])

        self.assertTrue(jnp.allclose(p_clusters_expected, p_clusters))

    def test_M_step(self):
        X = jnp.array([[1], [0], [0]])
        p_clusters = jnp.array([[0.1, 0.5, 0.6],
                                [0.9, 0.5, 0.4]])

        bmm = BMM(2)
        dist_params, p_mixture = bmm._M_step(p_clusters, X)

        p_mixture_expected = jnp.array([0.4, 0.6])
        dist_params_expected = jnp.array([[1/12], [0.5]])

        self.assertTrue(jnp.allclose(p_mixture_expected, p_mixture))
        self.assertTrue(jnp.allclose(dist_params_expected, dist_params))

    def test_predict_dummy(self):
        X = jnp.array([[1, 0],
                       [0, 1]])

        bmm = BMM(2)
        X_pred = bmm.fit_predict(X)
        cluster1 = X_pred[0]

        pred = bmm.predict(jnp.array([[1, 0], [0, 1]]))
        self.assertTrue(pred[0] == cluster1)
        self.assertTrue(pred[1] != cluster1)


# Copyright 2019 Xanadu Quantum Technologies Inc.

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#     http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
r"""
Python library
==============

.. currentmodule:: thewalrus

This is the top level module of the The Walrus Python interface,
containing functions for computing the hafnian, loop hafnian,
and torontonian of matrices.

Algorithm terminology
---------------------

Eigenvalue hafnian algorithm
    The algorithm described in
    *A faster hafnian formula for complex matrices and its benchmarking on a supercomputer*,
    :cite:`bjorklund2018faster`.
    This algorithm scales like :math:`\mathcal{O}(n^3 2^{n/2})`, and supports calculation of
    the loop hafnian.

Recursive hafnian algorithm
    The algorithm described in *Counting perfect matchings as fast as Ryser* :cite:`bjorklund2012counting`.
    This algorithm scales like :math:`\mathcal{O}(n^4 2^{n/2})`. This algorithm does not
    currently support the loop hafnian.

Repeating hafnian algorithm
    The algorithm described in *From moments of sum to moments of product*, :cite:`kan2008moments`.
    This method is more efficient for matrices with repeated rows and columns, and supports caclulation of
    the loop hafnian.

Approximate hafnian algorithm
    The algorithm described in *Polynomial time algorithms to approximate permanents and mixed discriminants
    within a simply exponential factor*, :cite:`barvinok1999polynomial`.
    This algorithm allows us to efficiently approximate the hafnian of
    matrices with non-negative elements. This is done by sampling determinants;
    the larger the number of samples taken, the higher the accuracy.

Batched hafnian algorithm
    An algorithm that allows the calculation of hafnians of all reductions of
    a given matrix up to the cutoff (resolution) provided. Internally, this algorithm
    makes use of the multidimensional Hermite polynomials as per
    *The calculation of multidimensional Hermite polynomials and Gram-Charlier coefficients*
    :cite:`berkowitz1970calculation`.

Low-rank hafnian algorithm
    An algorithm that allows to calculate the hafnian of an :math:`r`-rank matrix :math:`\bm{A}` of size :math:`n \times n`
    by factorizing it as :math:`\bm{A} = \bm{G} \bm{G}^T` where :math:`\bm{G}` is of size :math:`n \times r`. The algorithm
    is described in Appendix C of
    *A faster hafnian formula for complex matrices and its benchmarking on a supercomputer*,
    :cite:`bjorklund2018faster`.

Python wrappers
---------------

.. autosummary::
    hafnian
    hafnian_repeated
    hafnian_batched
    tor
    perm
    permanent_repeated
    hermite_multidimensional

Pure Python functions
---------------------

.. autosummary::
    reduction
    version
    low_rank_hafnian
"""
# pylint: disable=wrong-import-position
import os
import platform

import numpy as np

import thewalrus.quantum

from ._hafnian import (
    haf_complex,
    haf_int,
    haf_real,
    haf_rpt_complex,
    haf_rpt_real,
    hafnian,
    hafnian_repeated,
    reduction,
)
from ._low_rank_haf import low_rank_hafnian
from ._hermite_multidimensional import hafnian_batched, hermite_multidimensional
from ._permanent import perm, perm_complex, perm_real, permanent_repeated
from ._torontonian import tor, threshold_detection_prob_displacement, threshold_detection_prob, numba_tor
from ._version import __version__

__all__ = [
    "hafnian",
    "hafnian_repeated",
    "hafnian_batched",
    "tor",
    "perm",
    "permanent_repeated",
    "reduction",
    "hermite_multidimensional",
    "version",
]

def version():
    r"""
    Get version number of The Walrus

    Returns:
      str: The package version number
    """
    return __version__


#!/usr/bin/env python3

###############################
# Mastering ML Python Mini Course
#
# Inspired by the project here:
#
# https://s3.amazonaws.com/MLMastery/machine_learning_mastery_with_python_mini_course.pdf?__s=mxhvphowryg2sfmzus2q
#
# By Nathan Fritter
#
# Project will soon be found at:
#
# https://www.inertia7.com/projects/
#
# General style for python:
#
# snake_case for functions and variables, PascalCase for classes
###############################

# Welcome to my repo for the Mastering Machine Learning Python Mini Course
# Here I will be going through each part of the course
# So you can get a feel of the different parts

# Import packages before anything else
import pandas as pd
import numpy as np
from pandas import read_csv
from numpy import loadtxt
import urllib
import csv

# Define url and columns
# Define url and columns
url = 'https://goo.gl/bDdBiA'
columns = np.array(['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class'])

# Lesson 2: Loading in Data through different methods
'''
This script will load in the data using three different methods:
1. pandas.read_csv()
2. numpy.loadtxt()
	a. Make sure to specify the delimiter, otherwise it throws an error
3. csv.reader()
	a.Original method can only read physical csvs
	b. We need to include the urllib.request.urlopen() method for the url
	c. THEN, to properly load the data, we need to create a generator object using codecs
	d. Then convert to a list, THEN to a DataFrame in pandas. Having fun yet?
'''

# Method 1
try:
    method = "pandas.read_csv()"
    print("\nLoading in data via the %s method using a url\n" % method)
    data = read_csv(url, names = columns)

    print("Data read successfully using the %s method" % method)

except Exception as e:
    print("Error message:", e)

# Method 2
try:
    method = "numpy.loadtxt()"
    print("\nLoading in data via the %s method using a url\n" % method)
    data = loadtxt(url, dtype = float)

    print("Data read successfully using the %s method" % method)

except Exception as e:
    print("Error message:", e)

# Method 3
try:
    method = "csv.reader()"
    print("\nLoading in data via the %s method using a url\n" % method)
    response = urllib.request.urlopen(url)
    data = csv.reader(codecs.iterdecode(response, 'utf-8'))
    data = pd.DataFrame(list(data), columns = columns)

    print("Data read successfully using the %s method" % method)


"""
Tests for statistical reductions of 2nd moment or higher: var, skew, kurt, ...
"""
import inspect

import numpy as np
import pytest

import pandas.util._test_decorators as td

import pandas as pd
from pandas import (
    DataFrame,
    Series,
)
import pandas._testing as tm
from pandas.core.arrays import (
    DatetimeArray,
    PeriodArray,
    TimedeltaArray,
)

class TestDatetimeLikeStatReductions:
    @pytest.mark.parametrize("box", [Series, pd.Index, DatetimeArray])
    def test_dt64_mean(self, tz_naive_fixture, box):
        tz = tz_naive_fixture

        dti = pd.date_range("2001-01-01", periods=11, tz=tz)
        # shuffle so that we are not just working with monotone-increasing
        dti = dti.take([4, 1, 3, 10, 9, 7, 8, 5, 0, 2, 6])
        dtarr = dti._data

        obj = box(dtarr)
        assert obj.mean() == pd.Timestamp("2001-01-06", tz=tz)
        assert obj.mean(skipna=False) == pd.Timestamp("2001-01-06", tz=tz)

        # dtarr[-2] will be the first date 2001-01-1
        dtarr[-2] = pd.NaT

        obj = box(dtarr)
        assert obj.mean() == pd.Timestamp("2001-01-06 07:12:00", tz=tz)
        assert obj.mean(skipna=False) is pd.NaT

    @pytest.mark.parametrize("box", [Series, pd.Index, PeriodArray])
    @pytest.mark.parametrize("freq", ["S", "H", "D", "W", "B"])
    def test_period_mean(self, box, freq):
        # GH#24757
        dti = pd.date_range("2001-01-01", periods=11)
        # shuffle so that we are not just working with monotone-increasing
        dti = dti.take([4, 1, 3, 10, 9, 7, 8, 5, 0, 2, 6])

        parr = dti._data.to_period(freq)
        obj = box(parr)
        with pytest.raises(TypeError, match="ambiguous"):
            obj.mean()
        with pytest.raises(TypeError, match="ambiguous"):
            obj.mean(skipna=True)

        # parr[-2] will be the first date 2001-01-1
        parr[-2] = pd.NaT

        with pytest.raises(TypeError, match="ambiguous"):
            obj.mean()
        with pytest.raises(TypeError, match="ambiguous"):
            obj.mean(skipna=True)

    @pytest.mark.parametrize("box", [Series, pd.Index, TimedeltaArray])
    def test_td64_mean(self, box):
        tdi = pd.TimedeltaIndex([0, 3, -2, -7, 1, 2, -1, 3, 5, -2, 4], unit="D")

        tdarr = tdi._data
        obj = box(tdarr)

        result = obj.mean()
        expected = np.array(tdarr).mean()
        assert result == expected

        tdarr[0] = pd.NaT
        assert obj.mean(skipna=False) is pd.NaT

        result2 = obj.mean(skipna=True)
        assert result2 == tdi[1:].mean()

        # exact equality fails by 1 nanosecond
        assert result2.round("us") == (result * 11.0 / 10).round("us")

class TestSeriesStatReductions:
    # Note: the name TestSeriesStatReductions indicates these tests
    #  were moved from a series-specific test file, _not_ that these tests are
    #  intended long-term to be series-specific

    def _check_stat_op(
        self, name, alternate, string_series_, check_objects=False, check_allna=False
    ):

        with pd.option_context("use_bottleneck", False):
            f = getattr(Series, name)

            # add some NaNs
            string_series_[5:15] = np.NaN

            # mean, idxmax, idxmin, min, and max are valid for dates
            if name not in ["max", "min", "mean", "median", "std"]:
                ds = Series(pd.date_range("1/1/2001", periods=10))
                msg = f"does not support reduction '{name}'"
                with pytest.raises(TypeError, match=msg):
                    f(ds)

            # skipna or no
            assert pd.notna(f(string_series_))
            assert pd.isna(f(string_series_, skipna=False))

            # check the result is correct
            nona = string_series_.dropna()
            tm.assert_almost_equal(f(nona), alternate(nona.values))
            tm.assert_almost_equal(f(string_series_), alternate(nona.values))

            allna = string_series_ * np.nan

            if check_allna:
                assert np.isnan(f(allna))

            # dtype=object with None, it works!
            s = Series([1, 2, 3, None, 5])
            f(s)

            # GH#2888
            items = [0]
            items.extend(range(2**40, 2**40 + 1000))
            s = Series(items, dtype="int64")
            tm.assert_almost_equal(float(f(s)), float(alternate(s.values)))

            # check date range
            if check_objects:
                s = Series(pd.bdate_range("1/1/2000", periods=10))
                res = f(s)
                exp = alternate(s)
                assert res == exp

            # check on string data
            if name not in ["sum", "min", "max"]:
                with pytest.raises(TypeError, match=None):
                    f(Series(list("abc")))

            # Invalid axis.
            msg = "No axis named 1 for object type Series"
            with pytest.raises(ValueError, match=msg):
                f(string_series_, axis=1)

            # Unimplemented numeric_only parameter.
            if "numeric_only" in inspect.getfullargspec(f).args:
                with pytest.raises(NotImplementedError, match=name):
                    f(string_series_, numeric_only=True)

    def test_sum(self):
        string_series = tm.makeStringSeries().rename("series")
        self._check_stat_op("sum", np.sum, string_series, check_allna=False)

    def test_mean(self):
        string_series = tm.makeStringSeries().rename("series")
        self._check_stat_op("mean", np.mean, string_series)

    def test_median(self):
        string_series = tm.makeStringSeries().rename("series")
        self._check_stat_op("median", np.median, string_series)

        # test with integers, test failure
        int_ts = Series(np.ones(10, dtype=int), index=range(10))
        tm.assert_almost_equal(np.median(int_ts), int_ts.median())

    def test_prod(self):
        string_series = tm.makeStringSeries().rename("series")
        self._check_stat_op("prod", np.prod, string_series)

    def test_min(self):
        string_series = tm.makeStringSeries().rename("series")
        self._check_stat_op("min", np.min, string_series, check_objects=True)

    def test_max(self):
        string_series = tm.makeStringSeries().rename("series")
        self._check_stat_op("max", np.max, string_series, check_objects=True)

    def test_var_std(self):
        string_series = tm.makeStringSeries().rename("series")
        datetime_series = tm.makeTimeSeries().rename("ts")

        alt = lambda x: np.std(x, ddof=1)
        self._check_stat_op("std", alt, string_series)

        alt = lambda x: np.var(x, ddof=1)
        self._check_stat_op("var", alt, string_series)

        result = datetime_series.std(ddof=4)
        expected = np.std(datetime_series.values, ddof=4)
        tm.assert_almost_equal(result, expected)

        result = datetime_series.var(ddof=4)
        expected = np.var(datetime_series.values, ddof=4)
        tm.assert_almost_equal(result, expected)

        # 1 - element series with ddof=1
        s = datetime_series.iloc[[0]]
        result = s.var(ddof=1)
        assert pd.isna(result)

        result = s.std(ddof=1)
        assert pd.isna(result)

    def test_sem(self):
        string_series = tm.makeStringSeries().rename("series")
        datetime_series = tm.makeTimeSeries().rename("ts")

        alt = lambda x: np.std(x, ddof=1) / np.sqrt(len(x))
        self._check_stat_op("sem", alt, string_series)

        result = datetime_series.sem(ddof=4)
        expected = np.std(datetime_series.values, ddof=4) / np.sqrt(
            len(datetime_series.values)
        )
        tm.assert_almost_equal(result, expected)

        # 1 - element series with ddof=1
        s = datetime_series.iloc[[0]]
        result = s.sem(ddof=1)
        assert pd.isna(result)

    @td.skip_if_no_scipy
    def test_skew(self):
        from scipy.stats import skew

        string_series = tm.makeStringSeries().rename("series")

        alt = lambda x: skew(x, bias=False)
        self._check_stat_op("skew", alt, string_series)

        # test corner cases, skew() returns NaN unless there's at least 3
        # values
        min_N = 3
        for i in range(1, min_N + 1):
            s = Series(np.ones(i))
            df = DataFrame(np.ones((i, i)))
            if i < min_N:
                assert np.isnan(s.skew())
                assert np.isnan(df.skew()).all()
            else:
                assert 0 == s.skew()
                assert (df.skew() == 0).all()

    @td.skip_if_no_scipy
    def test_kurt(self):
        from scipy.stats import kurtosis

        string_series = tm.makeStringSeries().rename("series")

        alt = lambda x: kurtosis(x, bias=False)
        self._check_stat_op("kurt", alt, string_series)

        index = pd.MultiIndex(
            levels=[["bar"], ["one", "two", "three"], [0, 1]],
            codes=[[0, 0, 0, 0, 0, 0], [0, 1, 2, 0, 1, 2], [0, 1, 0, 1, 0, 1]],
        )
        s = Series(np.random.randn(6), index=index)
        with tm.assert_produces_warning(FutureWarning):
            tm.assert_almost_equal(s.kurt(), s.kurt(level=0)["bar"])

        # test corner cases, kurt() returns NaN unless there's at least 4
        # values
        min_N = 4
        for i in range(1, min_N + 1):
            s = Series(np.ones(i))
            df = DataFrame(np.ones((i, i)))
            if i < min_N:
                assert np.isnan(s.kurt())
                assert np.isnan(df.kurt()).all()
            else:
                assert 0 == s.kurt()
                assert (df.kurt() == 0).all()


# -*- coding: utf-8 -*-
"""
Created on Fri Jul 27 10:08:25 2018

@author: cdeline

Using pytest to create unit tests for bifacial_radiance.

to run unit tests, run pytest from the command line in the bifacial_radiance directory
to run coverage tests, run py.test --cov-report term-missing --cov=bifacial_radiance

"""

#from bifacial_radiance import RadianceObj, SceneObj, AnalysisObj
import bifacial_radiance
import numpy as np
import pytest
import os

# try navigating to tests directory so tests run from here.
try:
    os.chdir('tests')
except:
    pass

TESTDIR = os.path.dirname(__file__)  # this folder

# test the readepw on a dummy Boulder EPW file in the /tests/ directory
MET_FILENAME =  'USA_CO_Boulder.724699_TMY2.epw'
# also test a dummy TMY3 Denver file in /tests/
MET_FILENAME2 = "724666TYA.CSV"

#def test_quickExample():
#    results = bifacial_radiance.main.quickExample(TESTDIR)
#    assert np.mean(results.Wm2Back) == pytest.approx(195380.94444444444, rel = 0.03)  # was 182 in v0.2.2

def test_RadianceObj_set1axis():
    # test set1axis.  requires metdata for boulder.
    name = "_test_set1axis"
    demo = bifacial_radiance.RadianceObj(name)
    try:
        epwfile = demo.getEPW(lat=40.01667, lon=-105.25)  # From EPW: {N 40°  1'} {W 105° 15'}
    except: # adding an except in case the internet connection in the lab forbids the epw donwload.
        epwfile = MET_FILENAME
    metdata = demo.readEPW(epwfile = epwfile)
    trackerdict = demo.set1axis()
    assert trackerdict[0]['count'] == 80 #this was 108 < v0.2.4 and 75 < 0.3.2
    assert trackerdict[45]['count'] == 822 #this was 823 < 0.3.2

def test_RadianceObj_fixed_tilt_end_to_end():
    # just run the demo example.  Rear irradiance fraction roughly 11.8% for 0.95m landscape panel
    # takes 12 seconds
    name = "_test_fixed_tilt_end_to_end"
    demo = bifacial_radiance.RadianceObj(name)  # Create a RadianceObj 'object'
    demo.setGround(0.62) # input albedo number or material name like 'concrete'.  To see options, run this without any input.

    metdata = demo.readEPW(epwfile= MET_FILENAME) # read in the EPW weather data from above
    #metdata = demo.readTMY() # select a TMY file using graphical picker
    # Now we either choose a single time point, or use cumulativesky for the entire year.
    fullYear = False
    if fullYear:
        demo.genCumSky(demo.epwfile) # entire year.
    else:
        demo.gendaylit(timeindex=4020, metdata=metdata)  # Noon, June 17th
    # create a scene using panels in landscape at 10 deg tilt, 1.5m pitch. 0.2 m ground clearance
    sceneDict = {'tilt':10,'pitch':1.5,'height':0.2, 'nMods':10, 'nRows':3}
    demo.makeModule(name='test',y=0.95,x=1.59, xgap=0)
    scene = demo.makeScene('test',sceneDict) #makeScene creates a .rad file with 20 modules per row, 7 rows.
    octfile = demo.makeOct(demo.getfilelist())  # makeOct combines all of the ground, sky and object files into a .oct file.
    analysis = bifacial_radiance.AnalysisObj(octfile, demo.name)  # return an analysis object including the scan dimensions for back irradiance
    (frontscan,backscan) = analysis.moduleAnalysis(scene)
    analysis.analysis(octfile, demo.name, frontscan, backscan)  # compare the back vs front irradiance
    #assert np.round(np.mean(analysis.backRatio),decimals=2) == 0.12  # NOTE: this value is 0.11 when your module size is 1m, 0.12 when module size is 0.95m
    assert np.mean(analysis.backRatio) == pytest.approx(0.12, abs = 0.01)

def test_Radiance_high_azimuth_modelchains():
    # duplicate next example using modelchain
    # high azimuth .ini file

    HIGH_AZIMUTH_INI = os.path.join(TESTDIR, "ini_highAzimuth.ini")

    (Params)= bifacial_radiance.load.readconfigurationinputfile(inifile=HIGH_AZIMUTH_INI)
    Params[0]['testfolder'] = TESTDIR
    # unpack the Params tuple with *Params
    demo2, analysis = bifacial_radiance.modelchain.runModelChain(*Params )
    #assert np.round(np.mean(analysis.backRatio),2) == 0.20  # bifi ratio was == 0.22 in v0.2.2
    assert np.mean(analysis.Wm2Front) == pytest.approx(899, rel = 0.005)  # was 912 in v0.2.3
    assert np.mean(analysis.Wm2Back) == pytest.approx(189, rel = 0.03)  # was 182 in v0.2.2

"""
def test_RadianceObj_high_azimuth_angle_end_to_end():
    # modify example for high azimuth angle to test different parts of _makeSceneNxR.  Rear irradiance fraction roughly 17.3% for 0.95m landscape panel
    # takes 14 seconds for sensorsy = 9, 11 seconds for sensorsy = 2
    name = "_test_high_azimuth_angle_end_to_end"
    demo = bifacial_radiance.RadianceObj(name)  # Create a RadianceObj 'object'
    demo.setGround('white_EPDM') # input albedo number or material name like 'concrete'.  To see options, run this without any input.

    #metdata = demo.readEPW() # read in the EPW weather data from above
    metdata = demo.readTMY(MET_FILENAME2) # select a TMY file using graphical picker
    # Now we either choose a single time point, or use cumulativesky for the entire year.
    fullYear = False
    if fullYear:
        demo.genCumSky(demo.epwfile) # entire year.  # Don't know how to test this yet in pytest...
    else:
        demo.gendaylit(metdata=metdata,timeindex=4020)  # Noon, June 17th
    # create a scene using panels in landscape at 10 deg tilt, 1.5m pitch. 0.2 m ground clearance
    sceneDict = {'tilt':10,'pitch':1.5,'height':0.2,'azimuth':30, 'nMods':10, 'nRows':3}
    moduleDict = demo.makeModule(name='test',y=0.95,x=1.59, xgap=0)
    scene = demo.makeScene('test',sceneDict) #makeScene creates a .rad file with 20 modules per row, 7 rows.
    octfile = demo.makeOct(demo.getfilelist())  # makeOct combines all of the ground, sky and object files into a .oct file.
    analysis = bifacial_radiance.AnalysisObj(octfile, demo.name)  # return an analysis object including the scan dimensions for back irradiance
    (frontscan,backscan) = analysis.moduleAnalysis(scene)
    analysis.analysis(octfile, demo.name, frontscan, backscan)  # compare the back vs front irradiance
    #assert np.round(np.mean(analysis.backRatio),2) == 0.20  # bifi ratio was == 0.22 in v0.2.2
    assert np.mean(analysis.Wm2Front) == pytest.approx(899, rel = 0.005)  # was 912 in v0.2.3
    assert np.mean(analysis.Wm2Back) == pytest.approx(189, rel = 0.02)  # was 182 in v0.2.2
"""

def test_Radiance_1axis_gendaylit_modelchains():
    # duplicate next sample using modelchain
    # 1-axis .ini file
    filename = "ini_1axis.ini"

    (Params)= bifacial_radiance.load.readconfigurationinputfile(inifile=filename)
    Params[0]['testfolder'] = TESTDIR
    # unpack the Params tuple with *Params
    demo2, analysis = bifacial_radiance.modelchain.runModelChain(*Params )
    #V 0.2.5 fixed the gcr passed to set1axis. (since gcr was not being passd to set1axis, gcr was default 0.33 default).
    assert(np.mean(demo2.Wm2Front) == pytest.approx(205.0, 0.01) ) # was 214 in v0.2.3  # was 205 in early v0.2.4
    assert(np.mean(demo2.Wm2Back) == pytest.approx(43.0, 0.1) )

"""
def test_RadianceObj_1axis_gendaylit_end_to_end():
    name = "_test_1axis_gendaylit_end_to_end"
    # 1-axis tracking end-to-end test with torque tube and gap generation.
    # Takes 20 seconds for 2-sensor scan
    gcr = 0.35   # ground cover ratio,  = module_height / pitch
    albedo = 0.3     # ground albedo
    hub_height = 2   # tracker height at 0 tilt in meters (hub height)

    demo = bifacial_radiance.RadianceObj(name)  # Create a RadianceObj 'object'
    demo.setGround(albedo) # input albedo number or material name like 'concrete'.  To see options, run this without any input.
    metdata = demo.readEPW(MET_FILENAME, starttime='01_01_01', endtime = '01_01_23') # read in the EPW weather data from above
    #metdata = demo.readTMY(MET_FILENAME2) # select a TMY file using graphical picker
    # set module type to be used and passed into makeScene1axis
    # test modules with gap and rear tube
    moduleDict=demo.makeModule(name='test',x=0.984,y=1.95,torquetube = True, numpanels = 2, ygap = 0.1)
    sceneDict = {'pitch': np.round(moduleDict['sceney'] / gcr,3),'height':hub_height, 'nMods':10, 'nRows':3}
    key = '01_01_11'
    # create metdata files for each condition. keys are timestamps for gendaylit workflow
    trackerdict = demo.set1axis(cumulativesky = False, gcr=gcr)
    # create the skyfiles needed for 1-axis tracking
    demo.gendaylit1axis(metdata = metdata, enddate = '01/01')
    # Create the scene for the 1-axis tracking
    demo.makeScene1axis({key:trackerdict[key]}, moduletype='test', sceneDict=sceneDict, cumulativesky = False)
    #demo.makeScene1axis({key:trackerdict[key]}, module_type,sceneDict, cumulativesky = False, nMods = 10, nRows = 3, modwanted = 7, rowwanted = 3, sensorsy = 2) #makeScene creates a .rad file with 20 modules per row, 7 rows.

    demo.makeOct1axis(trackerdict,key) # just run this for one timestep: Jan 1 11am
    trackerdict = demo.analysis1axis(trackerdict, singleindex=key, modWanted=7, rowWanted=3, sensorsy=2) # just run this for one timestep: Jan 1 11am

    #V 0.2.5 fixed the gcr passed to set1axis. (since gcr was not being passd to set1axis, gcr was default 0.33 default).
    assert(np.mean(demo.Wm2Front) == pytest.approx(205.0, 0.01) ) # was 214 in v0.2.3  # was 205 in early v0.2.4
    assert(np.mean(demo.Wm2Back) == pytest.approx(43.0, 0.1) )
"""

def test_1axis_gencumSky():
    name = "test_1axis_gencumSky"
    # Takes 20 seconds for 2-sensor scan
    gcr = 0.35   # ground cover ratio,  = module_height / pitch
    albedo = 0.3     # ground albedo
    hub_height = 2   # tracker height at 0 tilt in meters (hub height)

    demo = bifacial_radiance.RadianceObj(name)  # Create a RadianceObj 'object'
    demo.setGround(albedo) # input albedo number or material name like 'concrete'.  To see options, run this without any input.
    demo.readEPW(MET_FILENAME, starttime='01_01_01', endtime = '01_01_23') # read in the EPW weather data from above
    moduleDict=demo.makeModule(name='test',x=0.984,y=1.95, numpanels = 2, ygap = 0.1)
    pitch= np.round(moduleDict['sceney'] / gcr,3)
    trackerdict = demo.set1axis(cumulativesky = True, gcr=gcr)
    demo.genCumSky1axis()
    assert trackerdict[-45.0]['skyfile'][0:5] == 'skies' #  # Having trouble with the \ or //    'skies\\1axis_-45.0.rad'
    sceneDict = {'gcr': gcr,'hub_height':hub_height, 'clearance_height':hub_height, 'nMods':10, 'nRows':3}
    trackerdict = demo.makeScene1axis(sceneDict=sceneDict, moduletype = 'test')
    # Removing all of this other tests for hub_height and height since it's ben identified that
    # a new module to handle hub_height and height in sceneDict needs to be implemented
    # instead of checking inside of makeScene, makeSceneNxR, and makeScene1axis
    assert trackerdict[-5.0]['radfile'][0:7] == 'objects' # 'objects\\1axis-5.0_1.825_11.42_5.0_10x3_origin0,0.rad'
    sceneDict = {'pitch': pitch,'clearance_height':hub_height, 'nMods':10, 'nRows':3}  # testing height filter too
    trackerdict = demo.makeScene1axis(sceneDict=sceneDict, moduletype = 'test')
#    assert trackerdict[-5.0]['radfile'] == 'objects\\1axis-5.0_1.825_11.42_5.0_10x3_origin0,0.rad'
    sceneDict = {'pitch': pitch,'height':hub_height, 'nMods':10, 'nRows':3}  # testing height filter too
    trackerdict = demo.makeScene1axis(sceneDict=sceneDict, moduletype = 'test')
#    assert trackerdict[-5.0]['radfile'] == 'objects\\1axis-5.0_1.825_11.42_5.0_10x3_origin0,0.rad'
    sceneDict = {'pitch': pitch,'height':hub_height, 'clearance_height':hub_height, 'nMods':10, 'nRows':3}  # testing height filter too
    trackerdict = demo.makeScene1axis(sceneDict=sceneDict, moduletype = 'test')
#    assert trackerdict[-5.0]['radfile'] == 'objects\\1axis-5.0_1.825_11.42_5.0_10x3_origin0,0.rad'
    sceneDict = {'pitch': pitch,'height':hub_height, 'hub_height':hub_height, 'nMods':10, 'nRows':3}  # testing height filter too
    trackerdict = demo.makeScene1axis(sceneDict=sceneDict, moduletype = 'test')
    demo.exportTrackerDict(trackerdict, savefile = 'results\exportedTrackerDict')
    assert trackerdict[-5.0]['radfile'][0:7] == 'objects'
    #assert trackerdict[-5.0]['radfile'] == 'objects\\1axis-5.0_1.825_11.42_5.0_10x3_origin0,0.rad'
#    trackerdict = demo.makeOct1axis(trackerdict=trackerdict) # just run this for one timestep: Jan 1 11am
#    trackerdict = demo.analysis1axis(trackerdict=trackerdict, modWanted=7, rowWanted=3, sensorsy=2)

def test_SceneObj_makeSceneNxR_lowtilt():
    # test _makeSceneNxR(tilt, height, pitch, azimuth = 180, nMods = 20, nRows = 7, radname = None)
    # default scene with simple_panel, 10 degree tilt, 0.2 height, 1.5 row spacing, landscape
    name = "_test_makeSceneNxR_lowtilt"
    demo = bifacial_radiance.RadianceObj(name)
    demo.makeModule(name='test',y=0.95,x=1.59)
    #scene = bifacial_radiance.SceneObj(moduletype = name)
    #scene._makeSceneNxR(tilt=10,height=0.2,pitch=1.5)
    sceneDict={'tilt':10, 'height':0.2, 'pitch':1.5}
    scene = demo.makeScene(moduletype='test', sceneDict=sceneDict)
    analysis = bifacial_radiance.AnalysisObj()
    (frontscan,backscan) = analysis.moduleAnalysis(scene)

    assert frontscan.pop('orient') == '-0.000 0.174 -0.985'# was 0,0,-11 in v0.2.4
    assert frontscan == pytest.approx({'Nx': 1, 'Ny': 9, 'Nz': 1,  'xinc': 0,  'yinc': 0.093556736536159757,
                              'xstart': 4.627616431348303e-17,'ystart': -0.3778735578756446, 'zinc': 0.016496576878358378, 'zstart': 0.23717753969161476})

    assert backscan.pop('orient') == '0.000 -0.174 0.985' # was 0,0,1 in v0.2.4
    assert backscan == pytest.approx({'Nx': 1, 'Ny': 9, 'Nz': 1,  'xinc': 0, 'yinc': 0.093556736536159757,
                              'xstart': 4.580831740657635e-17,  'ystart': -0.3740532979669721, 'zinc': 0.016496576878358378,
                              'zstart': 0.21551176912534617}) # zstart was 0.01 and zinc was 0 in v0.2.2
    #assert scene.text == '!xform -rz -90 -t -0.795 0.475 0 -rx 10 -t 0 0 0.2 -a 20 -t 1.6 0 0 -a 7 -t 0 1.5 0 -i 1 -t -15.9 -4.5 0 -rz 0 objects\\simple_panel.rad'
    assert scene.text[0:116] == '!xform -rx 10 -t 0 0 0.2824828843917919 -a 20 -t 1.6 0 0 -a 7 -t 0 1.5 0 -i 1 -t -14.4 -4.5 0 -rz 0 -t 0 0 0 objects' #linux has different directory structure and will error here.

def test_SceneObj_makeSceneNxR_hightilt():
    # test _makeSceneNxR(tilt, height, pitch, orientation = None, azimuth = 180, nMods = 20, nRows = 7, radname = None)
    # default scene with simple_panel, 50 degree tilt, 0.2 height, 1.5 row spacing, landscape
    name = "_test__makeSceneNxR_hightilt"
    demo = bifacial_radiance.RadianceObj(name)
    demo.makeModule(name='test',y=0.95,x=1.59)
    #scene = bifacial_radiance.SceneObj(moduletype = name)
    #scene._makeSceneNxR(tilt=65,height=0.2,pitch=1.5,azimuth=89)
    sceneDict={'tilt':65, 'height':0.2, 'pitch':1.5, 'azimuth':89}
    scene = demo.makeScene(moduletype='test', sceneDict=sceneDict)
    analysis = bifacial_radiance.AnalysisObj()
    (frontscan,backscan) = analysis.moduleAnalysis(scene)

    temp = frontscan.pop('orient')
    '''
    assert [float(x) for x in temp.split(' ')] == pytest.approx([-0.999847695156, -0.0174524064373, 0])

    assert frontscan == pytest.approx({'Nx': 1, 'Ny': 1, 'Nz': 9, 'xinc': 0, 'xstart': 0, 'yinc': 0,
                                'ystart': 0, 'zinc': 0.086099239768481745,'zstart': 0.28609923976848173})

    temp2 = backscan.pop('orient')
    assert [float(x) for x in temp2.split(' ')] == pytest.approx([0.999847695156, 0.0174524064373, 0])
    assert backscan == pytest.approx({'Nx': 1, 'Ny': 1, 'Nz': 9, 'xinc': 0, 'xstart': -0.94985531039857163,
                            'yinc': 0, 'ystart': -0.016579786115419416, 'zinc': 0.086099239768481745, 'zstart': 0.28609923976848173})
    #assert scene.text == '!xform -rz -90 -t -0.795 0.475 0 -rx 65 -t 0 0 0.2 -a 20 -t 1.6 0 0 -a 7 -t 0 1.5 0 -i 1 -t -15.9 -4.5 0 -rz 91 objects\\simple_panel.rad'
    assert scene.text[0:93] == '!xform -rx 65 -t 0 0 0.2 -a 20 -t 1.6 0 0 -a 7 -t 0 1.5 0 -i 1 -t -16.0 -4.5 0 -rz 91 objects'
    '''
    assert [float(x) for x in temp.split(' ')] == pytest.approx([-0.906, -0.016, -0.423]) #was 0,0,-1 in v0.2.4

    assert frontscan == pytest.approx({'Nx': 1, 'Ny': 9, 'Nz': 1, 'xinc': -0.040142620018581696, 'xstart': 0.1796000448657153, 'yinc': -0.0007006920388131139,
                                'ystart': 0.0031349304442418674, 'zinc': 0.08609923976848174,'zstart':  0.2949742232650364})

    temp2 = backscan.pop('orient')
    assert [float(x) for x in temp2.split(' ')] == pytest.approx([0.906, 0.016, 0.423]) #was 0,0,1 in v0.2.4
    assert backscan == pytest.approx({'Nx': 1, 'Ny': 9, 'Nz': 1, 'xinc': -0.040142620018581696, 'xstart': 0.15966431032235584,
                            'yinc': -0.0007006920388131139, 'ystart': 0.0027869509033958163, 'zinc': 0.08609923976848174, 'zstart': 0.28567662150674106})
    #assert scene.text == '!xform -rz -90 -t -0.795 0.475 0 -rx 65 -t 0 0 0.2 -a 20 -t 1.6 0 0 -a 7 -t 0 1.5 0 -i 1 -t -15.9 -4.5 0 -rz 91 objects\\simple_panel.rad'
    assert scene.text[0:117] == '!xform -rx 65 -t 0 0 0.6304961988424087 -a 20 -t 1.6 0 0 -a 7 -t 0 1.5 0 -i 1 -t -14.4 -4.5 0 -rz 91 -t 0 0 0 objects'

def test_AnalysisObj_linePtsMake3D():
    # test linepts = linePtsMake3D(xstart,ystart,zstart,xinc,yinc,zinc,Nx,Ny,Nz,orient):
    analysis = bifacial_radiance.AnalysisObj()
    linepts = analysis._linePtsMake3D(0,0,0,1,1,1,1,2,3,'0 1 0')
    assert linepts == '0 0 0 0 1 0 \r1 1 1 0 1 0 \r0 0 0 0 1 0 \r1 1 1 0 1 0 \r0 0 0 0 1 0 \r1 1 1 0 1 0 \r' # v2.5.0 new linepts because now x and z also increase not only y.
    #assert linepts == '0 0 0 0 1 0 \r0 1 0 0 1 0 \r0 0 1 0 1 0 \r0 1 1 0 1 0 \r0 0 2 0 1 0 \r0 1 2 0 1 0 \r'

def test_CellLevelModule():
    # test the cell-level module generation
    name = "_test_CellLevelModule"
    demo = bifacial_radiance.RadianceObj(name)  # Create a RadianceObj 'object'
    cellParams = {'xcell':0.156, 'ycell':0.156, 'numcellsx':6, 'numcellsy':10,
                   'xcellgap':0.02, 'ycellgap':0.02}
    #moduleDict = demo.makeModule(name=name, cellLevelModule=True, xcell=0.156, rewriteModulefile=True, ycell=0.156,
    #                             numcellsx=6, numcellsy=10, xcellgap=0.02, ycellgap=0.02)
    moduleDict = demo.makeModule(name='test', rewriteModulefile=True, cellLevelModuleParams = cellParams)
    assert moduleDict['x'] == 1.036
    assert moduleDict['y'] == 1.74
    assert moduleDict['scenex'] == 1.046
    assert moduleDict['sceney'] == 1.74
    assert moduleDict['text'] == '! genbox black cellPVmodule 0.156 0.156 0.02 | xform -t -0.44 -0.87 0 -a 6 -t 0.176 0 0 -a 10 -t 0 0.176 0 -a 1 -t 0 1.74 0'

def test_TorqueTubes_Module():
    name = "_test_TorqueTubes"
    demo = bifacial_radiance.RadianceObj(name)  # Create a RadianceObj 'object'
    moduleDict = demo.makeModule(name='square', y=0.95,x=1.59, rewriteModulefile=True, torquetube=True, tubetype='square')
    assert moduleDict['x'] == 1.59
    assert moduleDict['text'] == '! genbox black square 1.59 0.95 0.02 | xform -t -0.795 -0.475 0 -a 1 -t 0 0.95 0\r\n! genbox Metal_Grey tube1 1.6 0.1 0.1 | xform -t -0.8 -0.05 -0.2'
    moduleDict = demo.makeModule(name='round', y=0.95,x=1.59, rewriteModulefile=True, torquetube=True, tubetype='round')
    assert moduleDict['text'][0:30] == '! genbox black round 1.59 0.95'
    moduleDict = demo.makeModule(name='hex', y=0.95,x=1.59, rewriteModulefile=True, torquetube=True, tubetype='hex')
    assert moduleDict['text'][0:30] == '! genbox black hex 1.59 0.95 0'
    moduleDict = demo.makeModule(name='oct', y=0.95,x=1.59, rewriteModulefile=True, torquetube=True, tubetype='oct')
    assert moduleDict['text'][0:30] == '! genbox black oct 1.59 0.95 0'

def test_gendaylit2manual():
    name = "_test_gendaylit2manual"
    demo = bifacial_radiance.RadianceObj(name)
    demo.setGround('litesoil')
    skyname = demo.gendaylit2manual(dni = 700, dhi = 100, sunalt = 67, sunaz = 180) # Invented values.
    assert skyname[0:5] == 'skies' # Having trouble with the \ or // with 'skies\sky2__test_gendaylit2manual.rad'

def test_SingleModule_end_to_end():
    # 1 module for STC conditions. DNI:900, DHI:100, sun angle: 33 elevation 0 azimuth
    name = "_test_SingleModule_end_to_end"
    demo = bifacial_radiance.RadianceObj(name)  # Create a RadianceObj 'object'
    demo.setGround('litesoil')
    metdata = demo.readEPW(epwfile= MET_FILENAME)
    demo.gendaylit(timeindex=4020, metdata=metdata, debug=True)  # 1pm, June 17th
    # create a scene using panels in landscape at 10 deg tilt, 1.5m pitch. 0.2 m ground clearance
    tilt=demo.getSingleTimestampTrackerAngle(metdata=metdata, timeindex=4020, gcr=0.33)
    assert tilt == pytest.approx(-6.7, abs = 0.4)
    sceneDict = {'tilt':0,'pitch':1.5,'clearance_height':1, 'nMods':1, 'nRows':1}
    demo.makeModule()
    demo.makeModule(name='test',y=0.95,x=1.59, xgap=0)
    scene = demo.makeScene('test',sceneDict)

    #objname='Marker'
    #text='! genbox white_EPDM mymarker 0.02 0.02 2.5 | xform -t -.01 -.01 0'
    #customObject = demo.makeCustomObject(objname,text)
    #demo.appendtoScene(scene.radfiles, customObject, '!xform -rz 0')
    octfile = demo.makeOct(demo.getfilelist(), hpc=True)  # makeOct combines all of the ground, sky and object files into a .oct file.
    analysis = bifacial_radiance.AnalysisObj(octfile, demo.name)  # return an analysis object including the scan dimensions for back irradiance
    (frontscan,backscan) = analysis.moduleAnalysis(scene, sensorsy=1)
    analysis.analysis(octfile, demo.name, frontscan, backscan)  # compare the back vs front irradiance
    assert analysis.mattype[0][:12] == 'a0.0.a0.test'
    assert analysis.rearMat[0][:12] == 'a0.0.a0.test'
    assert analysis.x == [0]
    assert analysis.y == [0]
    assert np.mean(analysis.Wm2Front) == pytest.approx(1025, abs = 2)
    analysis.makeImage('side.vp', hpc=True)
    analysis.makeFalseColor('side.vp') #TODO: this works on silvanas computer,
    # side.vp must exist inside of views folder in test folder... make sure this works
    # in other computers
    assert np.mean(analysis.Wm2Back) == pytest.approx(166, abs = 6)

def test_left_label_metdata():
    # left labeled MetObj read in with -1 hour timedelta should be identical to
    # right labeled MetObj
    import pvlib
    import pandas as pd
    (tmydata, metadata) = pvlib.iotools.epw.read_epw(MET_FILENAME, coerce_year=2001)
    # rename different field parameters to match output from
    # pvlib.tmy.readtmy: DNI, DHI, DryBulb, Wspd
    tmydata.rename(columns={'dni':'DNI',
                            'dhi':'DHI',
                            'temp_air':'DryBulb',
                            'wind_speed':'Wspd',
                            'ghi':'GHI',
                            'albedo':'Alb'
                            }, inplace=True)
    metdata1 = bifacial_radiance.MetObj(tmydata, metadata, label='left')
    demo = bifacial_radiance.RadianceObj('test')
    metdata2 = demo.readEPW(epwfile=MET_FILENAME, label='right' )
    pd.testing.assert_frame_equal(metdata1.solpos, metdata2.solpos)
    assert metdata2.solpos.index[7] == pd.to_datetime('2001-01-01 07:42:00 -7')


import numpy as np

# イテラブル、イテレータとmap() の使いかた
# 参考URL: https://dot-blog.jp/news/python-iterable-iterator/

# イテラブル(Iterable)とイテレータ(Iterator)

# イテラブル(Iterable): for 文の in に書き込めるオブジェクト... Iterable は「繰り返し可能な」という意味
# 参考URL: https://python.ms/iterable/

# 以下、公式ドキュメントの日本語訳を引用：
# 一度に一つずつ、自分が持つ要素を返すことができるオブジェクトです。iterable の例には、次の型に属するオブジェクトが含まれます
# まず list, str, tuple などの全てのシーケンス型や、また dict, file object などのシーケンスでない型、
# あるいはユーザが __iter__ メソッドもしくはシーケンスの動作をする __getitem__ メソッドを実装した全てのクラスです

for v in [1, 2, 3]:
	print(v) # 1 -> 2 -> 3 と、順番が記憶されているので、listはIterableといえる
print()

# イテレータ(Iterator): list, tuple, set などの集合を表現するオブジェクトを  iter 関数  を使って  コピー したようなもの
# 参考URL: https://python.ms/iterator/

# イテレータは、組み込み関数 next() を通じて、含まれる要素を一つずつ取り出すことができる -> つまり、「次の要素」を記憶しているオブジェクト
# 『イテラブルなオブジェクト』と『イテレータ』の違いは『イテレーションした状態を記憶しておく』ことができること
my_iterator = iter([1, 2, 3, 4])
print("-------- print iterator --------")
print(next(my_iterator)) # -> 1
print(next(my_iterator)) # -> 2
print(next(my_iterator)) # -> 3
print(next(my_iterator)) # -> 4
print()

# 最後まで到達した状態で、next()を呼ばれると例外を発生する
# print(next(my_iterator)) # -> StopIteration

# map(callable, *iterable)
# iterableに入れられた繰り返し可能なオブジェクトの要素それぞれに対して、callableで渡されたオブジェクトの適用結果を、イテレータ(mapオブジェクト)として返す
# 参考URL: https://qiita.com/conf8o/items/0cb02bc504b51af09099

# 返り値をfor xxx in ループのオブジェクトに入れられる　->　返り値はIterableなオブジェクト（っていうかイテレータ）
print("-------- print result of map(np.square, [1, 3, 5, 7, 9]) --------")
for i, v in enumerate( map(np.square, [1, 3, 5, 7, 9]) ):
	print("{}: {}".format(i, v))
print()

# 与えられたリストの各要素を2乗した要素をふくむイテレータ(mapオブジェクト)を返す
num_square = map(np.square, [1, 3, 5, 7, 9])

# そのままだとmapオブジェクトなので、print()で中身を表示はできない
print("return map() is: ", end="")
print(num_square)
print("return type of map(): {}".format(type(num_square))) # type()で見てみる

print()

# listに変換すれば、printで一覧を表示できる
print("print(list(num_square)): -> ", end="")
print(list(num_square))

# ！！！ ここで注意 ！！！
# mapオブジェクトはイテレータであるので、最初の位置には戻れない
# ここでもう一回print()をしてみると、、、すでに末尾に達しているので何も表示されない
print("once more print(list(num_square)): -> ", end="")
print(list(num_square)) # -> []
print()

# mapの処理結果を何度も使いたい場合は、listなどにキャストしておく必要がある
num_square2 = list(map(np.square, [1, 3, 5, 7, 9]))

print("print(num_square2): -> ", end="")
print(list(num_square2))
print("once more print(num_square2): -> ", end="")
print(list(num_square2)) # -> 2回目でもちゃんと結果が出てくる
print()