text:
package storage

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/db"
	deviceConfig "github.com/lxc/lxd/lxd/device/config"
	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/lxd/migration"
	"github.com/lxc/lxd/lxd/node"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/rsync"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/lxd/storage/drivers"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/ioprogress"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/logging"
	"github.com/lxc/lxd/shared/units"
	"github.com/lxc/lxd/shared/validate"
)

// ValidName validates the provided name, and returns an error if it's not a valid storage name.
func ValidName(value string) error {
	if strings.Contains(value, "/") {
		return fmt.Errorf(`Storage name cannot contain "/"`)
	}

	return nil
}

// ConfigDiff returns a diff of the provided configs. Additionally, it returns whether or not
// only user properties have been changed.
func ConfigDiff(oldConfig map[string]string, newConfig map[string]string) ([]string, bool) {
	changedConfig := []string{}
	userOnly := true
	for key := range oldConfig {
		if oldConfig[key] != newConfig[key] {
			if !strings.HasPrefix(key, "user.") {
				userOnly = false
			}

			if !shared.StringInSlice(key, changedConfig) {
				changedConfig = append(changedConfig, key)
			}
		}
	}

	for key := range newConfig {
		if oldConfig[key] != newConfig[key] {
			if !strings.HasPrefix(key, "user.") {
				userOnly = false
			}

			if !shared.StringInSlice(key, changedConfig) {
				changedConfig = append(changedConfig, key)
			}
		}
	}

	// Skip on no change
	if len(changedConfig) == 0 {
		return nil, false
	}

	return changedConfig, userOnly
}

// VolumeTypeNameToDBType converts a volume type string to internal volume type DB code.
func VolumeTypeNameToDBType(volumeTypeName string) (int, error) {
	switch volumeTypeName {
	case db.StoragePoolVolumeTypeNameContainer:
		return db.StoragePoolVolumeTypeContainer, nil
	case db.StoragePoolVolumeTypeNameVM:
		return db.StoragePoolVolumeTypeVM, nil
	case db.StoragePoolVolumeTypeNameImage:
		return db.StoragePoolVolumeTypeImage, nil
	case db.StoragePoolVolumeTypeNameCustom:
		return db.StoragePoolVolumeTypeCustom, nil
	}

	return -1, fmt.Errorf("Invalid storage volume type name")
}

// VolumeTypeToDBType converts volume type to internal volume type DB code.
func VolumeTypeToDBType(volType drivers.VolumeType) (int, error) {
	switch volType {
	case drivers.VolumeTypeContainer:
		return db.StoragePoolVolumeTypeContainer, nil
	case drivers.VolumeTypeVM:
		return db.StoragePoolVolumeTypeVM, nil
	case drivers.VolumeTypeImage:
		return db.StoragePoolVolumeTypeImage, nil
	case drivers.VolumeTypeCustom:
		return db.StoragePoolVolumeTypeCustom, nil
	}

	return -1, fmt.Errorf("Invalid storage volume type")
}

// VolumeDBTypeToType converts internal volume type DB code to storage driver volume type.
func VolumeDBTypeToType(volDBType int) (drivers.VolumeType, error) {
	switch volDBType {
	case db.StoragePoolVolumeTypeContainer:
		return drivers.VolumeTypeContainer, nil
	case db.StoragePoolVolumeTypeVM:
		return drivers.VolumeTypeVM, nil
	case db.StoragePoolVolumeTypeImage:
		return drivers.VolumeTypeImage, nil
	case db.StoragePoolVolumeTypeCustom:
		return drivers.VolumeTypeCustom, nil
	}

	return "", fmt.Errorf("Invalid storage volume type")
}

// InstanceTypeToVolumeType converts instance type to storage driver volume type.
func InstanceTypeToVolumeType(instType instancetype.Type) (drivers.VolumeType, error) {
	switch instType {
	case instancetype.Container:
		return drivers.VolumeTypeContainer, nil
	case instancetype.VM:
		return drivers.VolumeTypeVM, nil
	}

	return "", fmt.Errorf("Invalid instance type")
}

// VolumeTypeToAPIInstanceType converts storage driver volume type to API instance type type.
func VolumeTypeToAPIInstanceType(volType drivers.VolumeType) (api.InstanceType, error) {
	switch volType {
	case drivers.VolumeTypeContainer:
		return api.InstanceTypeContainer, nil
	case drivers.VolumeTypeVM:
		return api.InstanceTypeVM, nil
	}

	return api.InstanceTypeAny, fmt.Errorf("Volume type doesn't have equivalent instance type")
}

// VolumeContentTypeToDBContentType converts volume type to internal code.
func VolumeContentTypeToDBContentType(contentType drivers.ContentType) (int, error) {
	switch contentType {
	case drivers.ContentTypeBlock:
		return db.StoragePoolVolumeContentTypeBlock, nil
	case drivers.ContentTypeFS:
		return db.StoragePoolVolumeContentTypeFS, nil
	}

	return -1, fmt.Errorf("Invalid volume content type")
}

// VolumeDBContentTypeToContentType converts internal content type DB code to driver representation.
func VolumeDBContentTypeToContentType(volDBType int) (drivers.ContentType, error) {
	switch volDBType {
	case db.StoragePoolVolumeContentTypeBlock:
		return drivers.ContentTypeBlock, nil
	case db.StoragePoolVolumeContentTypeFS:
		return drivers.ContentTypeFS, nil
	}

	return "", fmt.Errorf("Invalid volume content type")
}

// VolumeContentTypeNameToContentType converts volume content type string internal code.
func VolumeContentTypeNameToContentType(contentTypeName string) (int, error) {
	switch contentTypeName {
	case db.StoragePoolVolumeContentTypeNameFS:
		return db.StoragePoolVolumeContentTypeFS, nil
	case db.StoragePoolVolumeContentTypeNameBlock:
		return db.StoragePoolVolumeContentTypeBlock, nil
	}

	return -1, fmt.Errorf("Invalid volume content type name")
}

// VolumeDBCreate creates a volume in the database.
func VolumeDBCreate(s *state.State, pool Pool, projectName string, volumeName string, volumeDescription string, volumeType drivers.VolumeType, snapshot bool, volumeConfig map[string]string, expiryDate time.Time, contentType drivers.ContentType) error {
	// Convert the volume type to our internal integer representation.
	volDBType, err := VolumeTypeToDBType(volumeType)
	if err != nil {
		return err
	}

	volDBContentType, err := VolumeContentTypeToDBContentType(contentType)
	if err != nil {
		return err
	}

	// Check that a storage volume of the same storage volume type does not already exist.
	volumeID, _ := s.Cluster.GetStoragePoolNodeVolumeID(projectName, volumeName, volDBType, pool.ID())
	if volumeID > 0 {
		return fmt.Errorf("A storage volume %q of type %q already exists", fmt.Sprintf("%s_%s", projectName, volumeName), volumeType)
	}

	// Make sure that we don't pass a nil to the next function.
	if volumeConfig == nil {
		volumeConfig = map[string]string{}
	}

	volType, err := VolumeDBTypeToType(volDBType)
	if err != nil {
		return err
	}

	vol := drivers.NewVolume(pool.Driver(), pool.Name(), volType, contentType, volumeName, volumeConfig, pool.Driver().Config())

	// Fill default config.
	err = pool.Driver().FillVolumeConfig(vol)
	if err != nil {
		return err
	}

	// Validate config.
	err = pool.Driver().ValidateVolume(vol, false)
	if err != nil {
		return err
	}

	// Create the database entry for the storage volume.
	if snapshot {
		_, err = s.Cluster.CreateStorageVolumeSnapshot(projectName, volumeName, volumeDescription, volDBType, pool.ID(), vol.Config(), expiryDate)
	} else {
		_, err = s.Cluster.CreateStoragePoolVolume(projectName, volumeName, volumeDescription, volDBType, pool.ID(), vol.Config(), volDBContentType)
	}
	if err != nil {
		return fmt.Errorf("Error inserting volume %q for project %q in pool %q of type %q into database %q", volumeName, projectName, pool.Name(), volumeType, err)
	}

	return nil
}

// SupportedPoolTypes the types of pools supported.
// Deprecated: this is being replaced with drivers.SupportedDrivers()
var SupportedPoolTypes = []string{"btrfs", "ceph", "cephfs", "dir", "lvm", "zfs"}

// StorageVolumeConfigKeys config validation for btrfs, ceph, cephfs, dir, lvm, zfs types.
// Deprecated: these are being moved to the per-storage-driver implementations.
var StorageVolumeConfigKeys = map[string]func(value string) ([]string, error){
	"block.filesystem": func(value string) ([]string, error) {
		err := validate.Optional(validate.IsOneOf("btrfs", "ext4", "xfs"))(value)
		if err != nil {
			return nil, err
		}

		return []string{"ceph", "lvm"}, nil
	},
	"block.mount_options": func(value string) ([]string, error) {
		return []string{"ceph", "lvm"}, validate.IsAny(value)
	},
	"security.shifted": func(value string) ([]string, error) {
		return SupportedPoolTypes, validate.Optional(validate.IsBool)(value)
	},
	"security.unmapped": func(value string) ([]string, error) {
		return SupportedPoolTypes, validate.Optional(validate.IsBool)(value)
	},
	"size": func(value string) ([]string, error) {
		if value == "" {
			return SupportedPoolTypes, nil
		}

		_, err := units.ParseByteSizeString(value)
		if err != nil {
			return nil, err
		}

		return SupportedPoolTypes, nil
	},
	"volatile.idmap.last": func(value string) ([]string, error) {
		return SupportedPoolTypes, validate.IsAny(value)
	},
	"volatile.idmap.next": func(value string) ([]string, error) {
		return SupportedPoolTypes, validate.IsAny(value)
	},
	"zfs.remove_snapshots": func(value string) ([]string, error) {
		err := validate.Optional(validate.IsBool)(value)
		if err != nil {
			return nil, err
		}

		return []string{"zfs"}, nil
	},
	"zfs.use_refquota": func(value string) ([]string, error) {
		err := validate.Optional(validate.IsBool)(value)
		if err != nil {
			return nil, err
		}

		return []string{"zfs"}, nil
	},
}

// VolumeSnapshotsGet returns a list of snapshots of the form <volume>/<snapshot-name>.
func VolumeSnapshotsGet(s *state.State, projectName string, pool string, volume string, volType int) ([]db.StorageVolumeArgs, error) {
	poolID, err := s.Cluster.GetStoragePoolID(pool)
	if err != nil {
		return nil, err
	}

	snapshots, err := s.Cluster.GetLocalStoragePoolVolumeSnapshotsWithType(projectName, volume, volType, poolID)
	if err != nil {
		return nil, err
	}

	return snapshots, nil
}

// VolumePropertiesTranslate validates the supplied volume config and removes any keys that are not
// suitable for the volume's driver type.
func VolumePropertiesTranslate(targetConfig map[string]string, targetParentPoolDriver string) (map[string]string, error) {
	newConfig := make(map[string]string, len(targetConfig))
	for key, val := range targetConfig {
		// User keys are not validated.
		if strings.HasPrefix(key, "user.") {
			continue
		}

		// Validate storage volume config keys.
		validator, ok := StorageVolumeConfigKeys[key]
		if !ok {
			return nil, fmt.Errorf("Invalid storage volume configuration key: %s", key)
		}

		validStorageDrivers, err := validator(val)
		if err != nil {
			return nil, err
		}

		// Drop invalid keys.
		if !shared.StringInSlice(targetParentPoolDriver, validStorageDrivers) {
			continue
		}

		newConfig[key] = val
	}

	return newConfig, nil
}

// validatePoolCommonRules returns a map of pool config rules common to all drivers.
func validatePoolCommonRules() map[string]func(string) error {
	return map[string]func(string) error{
		"source":                  validate.IsAny,
		"volatile.initial_source": validate.IsAny,
		"volume.size":             validate.Optional(validate.IsSize),
		"size":                    validate.Optional(validate.IsSize),
		"rsync.bwlimit":           validate.IsAny,
		"rsync.compression":       validate.Optional(validate.IsBool),
	}
}

// validateVolumeCommonRules returns a map of volume config rules common to all drivers.
func validateVolumeCommonRules(vol drivers.Volume) map[string]func(string) error {
	rules := map[string]func(string) error{
		// Note: size should not be modifiable for non-custom volumes and should be checked
		// in the relevant volume update functions.
		"size": validate.Optional(validate.IsSize),
		"snapshots.expiry": func(value string) error {
			// Validate expression
			_, err := shared.GetSnapshotExpiry(time.Time{}, value)
			return err
		},
		"snapshots.schedule": validate.Optional(validate.IsCron([]string{"@hourly", "@daily", "@midnight", "@weekly", "@monthly", "@annually", "@yearly"})),
		"snapshots.pattern":  validate.IsAny,
	}

	// volatile.idmap settings only make sense for filesystem volumes.
	if vol.ContentType() == drivers.ContentTypeFS {
		rules["volatile.idmap.last"] = validate.IsAny
		rules["volatile.idmap.next"] = validate.IsAny
	}

	// block.mount_options and block.filesystem settings are only relevant for drivers that are block backed
	// and when there is a filesystem to actually mount. This includes filesystem volumes and VM Block volumes,
	// as they have an associated config filesystem volume that shares the config.
	if vol.IsBlockBacked() && (vol.ContentType() == drivers.ContentTypeFS || vol.IsVMBlock()) {
		rules["block.mount_options"] = validate.IsAny

		// Note: block.filesystem should not be modifiable after volume created.
		// This should be checked in the relevant volume update functions.
		rules["block.filesystem"] = validate.IsAny
	}

	// security.shifted and security.unmapped are only relevant for custom filesystem volumes.
	if vol.Type() == drivers.VolumeTypeCustom && vol.ContentType() == drivers.ContentTypeFS {
		rules["security.shifted"] = validate.Optional(validate.IsBool)
		rules["security.unmapped"] = validate.Optional(validate.IsBool)
	}

	// volatile.rootfs.size is only used for image volumes.
	if vol.Type() == drivers.VolumeTypeImage {
		rules["volatile.rootfs.size"] = validate.Optional(validate.IsInt64)
	}

	return rules
}

// ImageUnpack unpacks a filesystem image into the destination path.
// There are several formats that images can come in:
// Container Format A: Separate metadata tarball and root squashfs file.
// 	- Unpack metadata tarball into mountPath.
//	- Unpack root squashfs file into mountPath/rootfs.
// Container Format B: Combined tarball containing metadata files and root squashfs.
//	- Unpack combined tarball into mountPath.
// VM Format A: Separate metadata tarball and root qcow2 file.
// 	- Unpack metadata tarball into mountPath.
//	- Check rootBlockPath is a file and convert qcow2 file into raw format in rootBlockPath.
func ImageUnpack(imageFile string, vol drivers.Volume, destBlockFile string, blockBackend, runningInUserns bool, allowUnsafeResize bool, tracker *ioprogress.ProgressTracker) (int64, error) {
	logger := logging.AddContext(logger.Log, log.Ctx{"imageFile": imageFile, "vol": vol.Name()})

	// For all formats, first unpack the metadata (or combined) tarball into destPath.
	imageRootfsFile := imageFile + ".rootfs"
	destPath := vol.MountPath()

	// If no destBlockFile supplied then this is a container image unpack.
	if destBlockFile == "" {
		rootfsPath := filepath.Join(destPath, "rootfs")

		// Unpack the main image file.
		err := shared.Unpack(imageFile, destPath, blockBackend, runningInUserns, tracker)
		if err != nil {
			return -1, err
		}

		// Check for separate root file.
		if shared.PathExists(imageRootfsFile) {
			err = os.MkdirAll(rootfsPath, 0755)
			if err != nil {
				return -1, fmt.Errorf("Error creating rootfs directory")
			}

			err = shared.Unpack(imageRootfsFile, rootfsPath, blockBackend, runningInUserns, tracker)
			if err != nil {
				return -1, err
			}
		}

		// Check that the container image unpack has resulted in a rootfs dir.
		if !shared.PathExists(rootfsPath) {
			return -1, fmt.Errorf("Image is missing a rootfs: %s", imageFile)
		}

		// Done with this.
		return 0, nil
	}

	// If a rootBlockPath is supplied then this is a VM image unpack.

	// Validate the target.
	fileInfo, err := os.Stat(destBlockFile)
	if err != nil && !os.IsNotExist(err) {
		return -1, err
	}

	if fileInfo != nil && fileInfo.IsDir() {
		// If the dest block file exists, and it is a directory, fail.
		return -1, fmt.Errorf("Root block path isn't a file: %s", destBlockFile)
	}

	// convertBlockImage converts the qcow2 block image file into a raw block device. If needed it will attempt
	// to enlarge the destination volume to accommodate the unpacked qcow2 image file.
	convertBlockImage := func(v drivers.Volume, imgPath string, dstPath string) (int64, error) {
		// Get info about qcow2 file.
		imgJSON, err := shared.RunCommand("qemu-img", "info", "--output=json", imgPath)
		if err != nil {
			return -1, errors.Wrapf(err, "Failed reading image info %q", dstPath)
		}

		imgInfo := struct {
			Format      string `json:"format"`
			VirtualSize int64  `json:"virtual-size"`
		}{}

		err = json.Unmarshal([]byte(imgJSON), &imgInfo)
		if err != nil {
			return -1, err
		}

		if imgInfo.Format != "qcow2" {
			return -1, fmt.Errorf("Unexpected image format %q", imgInfo.Format)
		}

		// Check whether image is allowed to be unpacked into pool volume. Create a partial image volume
		// struct and then use it to check that target volume size can be set as needed.
		imgVolConfig := map[string]string{
			"volatile.rootfs.size": fmt.Sprintf("%d", imgInfo.VirtualSize),
		}
		imgVol := drivers.NewVolume(nil, "", drivers.VolumeTypeImage, drivers.ContentTypeBlock, "", imgVolConfig, nil)

		logger.Debug("Checking image unpack size")
		newVolSize, err := vol.ConfigSizeFromSource(imgVol)
		if err != nil {
			return -1, err
		}

		if shared.PathExists(dstPath) {
			volSizeBytes, err := drivers.BlockDiskSizeBytes(dstPath)
			if err != nil {
				return -1, errors.Wrapf(err, "Error getting current size of %q", dstPath)
			}

			// If the target volume's size is smaller than the image unpack size, then we need to
			// increase the target volume's size.
			if volSizeBytes < imgInfo.VirtualSize {
				logger.Debug("Increasing volume size", log.Ctx{"imgPath": imgPath, "dstPath": dstPath, "oldSize": volSizeBytes, "newSize": newVolSize})
				err = vol.SetQuota(newVolSize, allowUnsafeResize, nil)
				if err != nil {
					return -1, errors.Wrapf(err, "Error increasing volume size")
				}
			}
		}

		// Convert the qcow2 format to a raw block device using qemu's dd mode to avoid issues with
		// loop backed storage pools. Use the MinBlockBoundary block size to speed up conversion.
		logger.Debug("Converting qcow2 image to raw disk", log.Ctx{"imgPath": imgPath, "dstPath": dstPath})
		_, err = shared.RunCommand("qemu-img", "dd", "-f", "qcow2", "-O", "raw", fmt.Sprintf("bs=%d", drivers.MinBlockBoundary), fmt.Sprintf("if=%s", imgPath), fmt.Sprintf("of=%s", dstPath))
		if err != nil {
			return -1, errors.Wrapf(err, "Failed converting image to raw at %q", dstPath)
		}

		return imgInfo.VirtualSize, nil
	}

	var imgSize int64

	if shared.PathExists(imageRootfsFile) {
		// Unpack the main image file.
		err := shared.Unpack(imageFile, destPath, blockBackend, runningInUserns, tracker)
		if err != nil {
			return -1, err
		}

		// Convert the qcow2 format to a raw block device.
		imgSize, err = convertBlockImage(vol, imageRootfsFile, destBlockFile)
		if err != nil {
			return -1, err
		}
	} else {
		// Dealing with unified tarballs require an initial unpack to a temporary directory.
		tempDir, err := ioutil.TempDir(shared.VarPath("images"), "lxd_image_unpack_")
		if err != nil {
			return -1, err
		}
		defer os.RemoveAll(tempDir)

		// Unpack the whole image.
		err = shared.Unpack(imageFile, tempDir, blockBackend, runningInUserns, tracker)
		if err != nil {
			return -1, err
		}

		imgPath := filepath.Join(tempDir, "rootfs.img")

		// Convert the qcow2 format to a raw block device.
		imgSize, err = convertBlockImage(vol, imgPath, destBlockFile)
		if err != nil {
			return -1, err
		}

		// Delete the qcow2.
		err = os.Remove(imgPath)
		if err != nil {
			return -1, errors.Wrapf(err, "Failed to remove %q", imgPath)
		}

		// Transfer the content excluding the destBlockFile name so that we don't delete the block file
		// created above if the storage driver stores image files in the same directory as destPath.
		_, err = rsync.LocalCopy(tempDir, destPath, "", true, "--exclude", filepath.Base(destBlockFile))
		if err != nil {
			return -1, err
		}
	}

	return imgSize, nil
}

// InstanceContentType returns the instance's content type.
func InstanceContentType(inst instance.Instance) drivers.ContentType {
	contentType := drivers.ContentTypeFS
	if inst.Type() == instancetype.VM {
		contentType = drivers.ContentTypeBlock
	}

	return contentType
}

// VolumeUsedByProfileDevices finds profiles using a volume and passes them to profileFunc for evaluation.
// The profileFunc is provided with a profile config, project config and a list of device names that are using
// the volume.
func VolumeUsedByProfileDevices(s *state.State, poolName string, projectName string, vol *api.StorageVolume, profileFunc func(profile db.Profile, project db.Project, usedByDevices []string) error) error {
	// Convert the volume type name to our internal integer representation.
	volumeType, err := VolumeTypeNameToDBType(vol.Type)
	if err != nil {
		return err
	}

	projectMap := map[string]db.Project{}
	var profiles []db.Profile

	// Retrieve required info from the database in single transaction for performance.
	err = s.Cluster.Transaction(func(tx *db.ClusterTx) error {
		var err error

		projects, err := tx.GetProjects(db.ProjectFilter{})
		if err != nil {
			return errors.Wrap(err, "Failed loading projects")
		}

		// Index of all projects by name.
		for i, project := range projects {
			projectMap[project.Name] = projects[i]
		}

		profiles, err = tx.GetProfiles(db.ProfileFilter{})
		if err != nil {
			return errors.Wrap(err, "Failed loading profiles")
		}

		return nil
	})
	if err != nil {
		return err
	}

	// Iterate all profiles, consider only those which belong to a project that has the same effective
	// storage project as volume.
	for _, profile := range profiles {
		p := projectMap[profile.Project]
		profileStorageProject := project.StorageVolumeProjectFromRecord(&p, volumeType)
		if err != nil {
			return err
		}

		// Check profile's storage project is the same as the volume's project.
		// If not then the volume names mentioned in the profile's config cannot be referring to volumes
		// in the volume's project we are trying to match, and this profile cannot possibly be using it.
		if projectName != profileStorageProject {
			continue
		}

		var usedByDevices []string

		// Iterate through each of the profiles's devices, looking for disks in the same pool as volume.
		// Then try and match the volume name against the profile device's "source" property.
		for devName, dev := range profile.Devices {
			if dev["type"] != "disk" {
				continue
			}

			if dev["pool"] != poolName {
				continue
			}

			if dev["source"] == vol.Name {
				usedByDevices = append(usedByDevices, devName)
			}
		}

		if len(usedByDevices) > 0 {
			err = profileFunc(profile, p, usedByDevices)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

// VolumeUsedByInstanceDevices finds instances using a volume (either directly or via their expanded profiles if
// expandDevices is true) and passes them to instanceFunc for evaluation. If instanceFunc returns an error then it
// is returned immediately. The instanceFunc is executed during a DB transaction, so DB queries are not permitted.
// The instanceFunc is provided with a instance config, project config, instance's profiles and a list of device
// names that are using the volume.
func VolumeUsedByInstanceDevices(s *state.State, poolName string, projectName string, vol *api.StorageVolume, expandDevices bool, instanceFunc func(inst db.Instance, project db.Project, profiles []api.Profile, usedByDevices []string) error) error {
	// Convert the volume type name to our internal integer representation.
	volumeType, err := VolumeTypeNameToDBType(vol.Type)
	if err != nil {
		return err
	}

	return s.Cluster.InstanceList(nil, func(inst db.Instance, p db.Project, profiles []api.Profile) error {
		// If the volume has a specific cluster member which is different than the instance then skip as
		// instance cannot be using this volume.
		if vol.Location != "" && inst.Node != vol.Location {
			return nil
		}

		instStorageProject := project.StorageVolumeProjectFromRecord(&p, volumeType)
		if err != nil {
			return err
		}

		// Check instance's storage project is the same as the volume's project.
		// If not then the volume names mentioned in the instance's config cannot be referring to volumes
		// in the volume's project we are trying to match, and this instance cannot possibly be using it.
		if projectName != instStorageProject {
			return nil
		}

		// Use local devices for usage check by if expandDevices is false (but don't modify instance).
		devices := inst.Devices

		// Expand devices for usage check if expandDevices is true.
		if expandDevices {
			devices = db.ExpandInstanceDevices(deviceConfig.NewDevices(inst.Devices), profiles).CloneNative()
		}

		var usedByDevices []string

		// Iterate through each of the instance's devices, looking for disks in the same pool as volume.
		// Then try and match the volume name against the instance device's "source" property.
		for devName, dev := range devices {
			if dev["type"] != "disk" {
				continue
			}

			if dev["pool"] != poolName {
				continue
			}

			if dev["source"] == vol.Name {
				usedByDevices = append(usedByDevices, devName)
			}
		}

		if len(usedByDevices) > 0 {
			err = instanceFunc(inst, p, profiles, usedByDevices)
			if err != nil {
				return err
			}
		}

		return nil
	})
}

// VolumeUsedByExclusiveRemoteInstancesWithProfiles checks if custom volume is exclusively attached to a remote
// instance. Returns the remote instance that has the volume exclusively attached. Returns nil if volume available.
func VolumeUsedByExclusiveRemoteInstancesWithProfiles(s *state.State, poolName string, projectName string, vol *api.StorageVolume) (*db.Instance, error) {
	pool, err := GetPoolByName(s, poolName)
	if err != nil {
		return nil, errors.Wrapf(err, "Failed loading storage pool %q", poolName)
	}

	info := pool.Driver().Info()

	// Always return nil if the storage driver supports mounting volumes on multiple nodes at once.
	if info.VolumeMultiNode {
		return nil, nil
	}

	// Get local node name so we can check if the volume is attached to a remote node.
	var localNode string
	err = s.Cluster.Transaction(func(tx *db.ClusterTx) error {
		localNode, err = tx.GetLocalNodeName()
		if err != nil {
			return errors.Wrapf(err, "Failed to get local node name")
		}

		return nil
	})
	if err != nil {
		return nil, err
	}

	// Find if volume is attached to a remote instance.
	var remoteInstance *db.Instance
	err = VolumeUsedByInstanceDevices(s, poolName, projectName, vol, true, func(dbInst db.Instance, project db.Project, profiles []api.Profile, usedByDevices []string) error {
		if dbInst.Node != localNode {
			remoteInstance = &dbInst
			return db.ErrInstanceListStop // Stop the search, this volume is attached to a remote instance.
		}

		return nil
	})
	if err != nil && err != db.ErrInstanceListStop {
		return nil, err
	}

	return remoteInstance, nil
}

// VolumeUsedByDaemon indicates whether the volume is used by daemon storage.
func VolumeUsedByDaemon(s *state.State, poolName string, volumeName string) (bool, error) {
	var storageBackups string
	var storageImages string
	err := s.Node.Transaction(func(tx *db.NodeTx) error {
		nodeConfig, err := node.ConfigLoad(tx)
		if err != nil {
			return err
		}

		storageBackups = nodeConfig.StorageBackupsVolume()
		storageImages = nodeConfig.StorageImagesVolume()

		return nil
	})
	if err != nil {
		return false, err
	}

	fullName := fmt.Sprintf("%s/%s", poolName, volumeName)
	if storageBackups == fullName || storageImages == fullName {
		return true, nil
	}

	return false, nil
}

// FallbackMigrationType returns the fallback migration transport to use based on volume content type.
func FallbackMigrationType(contentType drivers.ContentType) migration.MigrationFSType {
	if contentType == drivers.ContentTypeBlock {
		return migration.MigrationFSType_BLOCK_AND_RSYNC
	}

	return migration.MigrationFSType_RSYNC
}

// RenderSnapshotUsage can be used as an optional argument to Instance.Render() to return snapshot usage.
// As this is a relatively expensive operation it is provided as an optional feature rather than on by default.
func RenderSnapshotUsage(s *state.State, snapInst instance.Instance) func(response interface{}) error {
	return func(response interface{}) error {
		apiRes, ok := response.(*api.InstanceSnapshot)
		if !ok {
			return nil
		}

		pool, err := GetPoolByInstance(s, snapInst)
		if err == nil {
			// It is important that the snapshot not be mounted here as mounting a snapshot can trigger a very
			// expensive filesystem UUID regeneration, so we rely on the driver implementation to get the info
			// we are requesting as cheaply as possible.
			apiRes.Size, _ = pool.GetInstanceUsage(snapInst)
		}

		return nil
	}
}

// InstanceMount mounts an instance's storage volume (if not already mounted).
// Please call InstanceUnmount when finished.
func InstanceMount(pool Pool, inst instance.Instance, op *operations.Operation) (*MountInfo, error) {
	var err error
	var mountInfo *MountInfo

	if inst.IsSnapshot() {
		mountInfo, err = pool.MountInstanceSnapshot(inst, op)
		if err != nil {
			return nil, err
		}
	} else {
		mountInfo, err = pool.MountInstance(inst, op)
		if err != nil {
			return nil, err
		}
	}

	return mountInfo, nil
}

// InstanceUnmount unmounts an instance's storage volume (if not in use). Returns if we unmounted the volume.
func InstanceUnmount(pool Pool, inst instance.Instance, op *operations.Operation) (bool, error) {
	var err error
	var ourUnmount bool

	if inst.IsSnapshot() {
		ourUnmount, err = pool.UnmountInstanceSnapshot(inst, op)
	} else {
		ourUnmount, err = pool.UnmountInstance(inst, op)
	}

	return ourUnmount, err
}

// InstanceDiskBlockSize returns the block device size for the instance's disk.
// This will mount the instance if not already mounted and will unmount at the end if needed.
func InstanceDiskBlockSize(pool Pool, inst instance.Instance, op *operations.Operation) (int64, error) {
	mountInfo, err := InstanceMount(pool, inst, op)
	if err != nil {
		return -1, err
	}
	defer InstanceUnmount(pool, inst, op)

	if mountInfo.DiskPath == "" {
		return -1, fmt.Errorf("No disk path available from mount")
	}

	blockDiskSize, err := drivers.BlockDiskSizeBytes(mountInfo.DiskPath)
	if err != nil {
		return -1, errors.Wrapf(err, "Error getting block disk size %q", mountInfo.DiskPath)
	}

	return blockDiskSize, nil
}

func syncSnapshotsVolumeGet(srcSnapshots []db.StorageVolumeArgs, destSnapshots []db.StorageVolumeArgs) ([]db.StorageVolumeArgs, []db.StorageVolumeArgs) {
	// There is currently no recorded creation timestamp, so we can only detect changes based on name.

	// Maps to track based on snapshot name (instead of vol/snapshot).
	srcSnapshotNames := map[string]db.StorageVolumeArgs{}
	destSnapshotNames := map[string]db.StorageVolumeArgs{}

	// Fill in the source snapshots.
	for _, snapshot := range srcSnapshots {
		// Strip the volume name.
		_, snapshotName, _ := shared.InstanceGetParentAndSnapshotName(snapshot.Name)

		srcSnapshotNames[snapshotName] = snapshot
	}

	// Fill in the destination snapshots.
	for _, snapshot := range destSnapshots {
		// Strip the volume name.
		_, snapshotName, _ := shared.InstanceGetParentAndSnapshotName(snapshot.Name)

		destSnapshotNames[snapshotName] = snapshot
	}

	// Result slices.
	syncSnapshots := []db.StorageVolumeArgs{}
	deleteSnapshots := []db.StorageVolumeArgs{}

	// Find destination snapshots to delete.
	for _, snapshot := range destSnapshots {
		_, snapshotName, _ := shared.InstanceGetParentAndSnapshotName(snapshot.Name)

		_, exists := srcSnapshotNames[snapshotName]
		if exists {
			continue
		}

		// Doesn't exist on source anymore, delete it.
		deleteSnapshots = append(deleteSnapshots, snapshot)
	}

	// Find source snapshots to sync.
	for _, snapshot := range srcSnapshots {
		_, snapshotName, _ := shared.InstanceGetParentAndSnapshotName(snapshot.Name)

		_, exists := destSnapshotNames[snapshotName]
		if exists {
			continue
		}

		// Doesn't exist on the target, sync it.
		syncSnapshots = append(syncSnapshots, snapshot)
	}

	return syncSnapshots, deleteSnapshots
}


text:
from django import forms
from django.db import models, transaction
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
from django.contrib import admin
from modeltranslation.admin import TranslationAdmin

from leaflet.admin import LeafletGeoAdmin
from tinymce.widgets import TinyMCE

from .models import (
    POIType, Theme, TrekNetwork, Usage, Route, DifficultyLevel, WebLink,
    WebLinkCategory, InformationDesk, Trek
)

class POITypeAdmin(TranslationAdmin):
    list_display = ('label', 'pictogram_img')
    search_fields = ('label',)

class ThemeAdmin(TranslationAdmin):
    list_display = ('label', 'pictogram_img')
    search_fields = ('label',)

class TrekNetworkAdmin(TranslationAdmin):
    list_display = ('network', 'pictogram_img')
    search_fields = ('network',)

class UsageAdmin(TranslationAdmin):
    list_display = ('usage', 'pictogram_img')
    search_fields = ('usage',)

class RouteAdmin(TranslationAdmin):
    list_display = ('route', 'pictogram_img')
    search_fields = ('route',)

class DifficultyLevelForm(forms.ModelForm):

    def clean_id(self):
        self.oldid = self.instance.pk if self.instance else None
        self.newid = self.cleaned_data.get('id')

        exists = len(DifficultyLevel.objects.filter(pk=self.newid)) > 0
        if self.oldid != self.newid and exists:
            raise ValidationError(_("Difficulty with id '%s' already exists") % self.newid)
        return self.newid

class DifficultyLevelAdmin(TranslationAdmin):
    form = DifficultyLevelForm
    list_display = ('id', 'difficulty', 'pictogram_img')
    search_fields = ('difficulty',)
    fields = ('id', 'difficulty', 'pictogram')

    def save_model(self, request, obj, form, change):
        """
        Allows to change DifficultyLevel id from Admin form.
        It will migrate all treks using this difficulty to the new id.
        """
        self.oldid = None

        # Nominal case. No migration.
        if form.oldid is None or form.oldid == form.newid:
            obj.save()
            return

        with transaction.atomic():
            # Migrate Treks
            migrated = []
            for t in Trek.objects.filter(difficulty=form.oldid):
                t.difficulty = None
                t.save()
                migrated.append(t)
            # Apply id change
            self.oldid = form.oldid
            obj.save()  # Will create new row in DB
            old = DifficultyLevel.objects.get(id=self.oldid)
            old.delete()
            # Restore
            for t in migrated:
                t.difficulty = obj
                t.save()

    def response_change(self, request, obj):
        """
        If id was changed, always returns to the list (prevent 404).
        Otherwise, behave as usual.
        """
        if self.oldid is not None:
            msg = _('Difficulty id %s was changed to %s successfully.') % (
                self.oldid, obj.pk)
            self.message_user(request, msg)
            return self.response_post_save_change(request, obj)
        return super(DifficultyLevelAdmin, self).response_change(request, obj)

class WebLinkAdmin(TranslationAdmin):
    list_display = ('name', 'url', )
    search_fields = ('name', 'url', )

class WebLinkCategoryAdmin(TranslationAdmin):
    list_display = ('label', 'pictogram_img')
    search_fields = ('label', )

class InformationDeskAdmin(LeafletGeoAdmin, TranslationAdmin):
    list_display = ('name', 'website', 'municipality')
    search_fields = ('name',)

    formfield_overrides = {
        models.TextField: {'widget': TinyMCE},
    }

# Register previously defined modeladmins
trek_admin_to_register = [
    (POIType, POITypeAdmin),
    (Theme, ThemeAdmin),
    (TrekNetwork, TrekNetworkAdmin),
    (Usage, UsageAdmin),
    (Route, RouteAdmin),
    (DifficultyLevel, DifficultyLevelAdmin),
    (WebLink, WebLinkAdmin),
    (WebLinkCategory, WebLinkCategoryAdmin),
    (InformationDesk, InformationDeskAdmin),
]

for model, model_admin in trek_admin_to_register:
    admin.site.register(model, model_admin)

redpajama_set_name: RedPajamaGithub


text:
description: Robotics was on display at the Tempe Campus' "Night of the Open Door"
---

Night of the Open Door: Polytechnic Campus from Arizona State University on Vimeo.

[ASU Now Link](https://asunow.asu.edu/20170226-sun-devil-life-asu-night-open-door-finale-tempe-campus)


text:
from __future__ import unicode_literals

import copy
import sys

if sys.version_info[0] == 2:
    from future_builtins import map
    str = unicode

import pytest

from pyte import Screen, Stream, mo
from pyte.screens import Char

# Test helpers.

def update(screen, lines, colored=[]):
    """Updates a given screen object with given lines, colors each line
    from ``colored`` in "red" and returns the modified screen.
    """
    for y, line in enumerate(lines):
        for x, char in enumerate(line):
            if y in colored:
                attrs = {"fg": "red"}
            else:
                attrs = {}
            screen.buffer[y][x] = Char(data=char, **attrs)

    return screen

# Tests.

def test_remove_non_existant_attribute():
    screen = Screen(2, 2)
    assert screen.buffer == [[screen.default_char, screen.default_char]] * 2

    screen.select_graphic_rendition(24)  # underline-off.
    assert screen.buffer == [[screen.default_char, screen.default_char]] * 2
    assert not screen.cursor.attrs.underscore

def test_attributes():
    screen = Screen(2, 2)
    assert screen.buffer == [[screen.default_char, screen.default_char]] * 2
    screen.select_graphic_rendition(1) # Bold

    # Still default, since we haven't written anything.
    assert screen.buffer == [[screen.default_char, screen.default_char]] * 2
    assert screen.cursor.attrs.bold

    screen.draw("f")
    assert screen.buffer == [
        [Char("f", "default", "default", bold=True), screen.default_char],
        [screen.default_char, screen.default_char]
    ]

def test_colors():
    screen = Screen(2, 2)
    assert screen.buffer == [[screen.default_char, screen.default_char]] * 2

    screen.select_graphic_rendition(30) # black foreground
    screen.select_graphic_rendition(40) # black background
    assert screen.cursor.attrs.fg == "black"
    assert screen.cursor.attrs.bg == "black"

    screen.select_graphic_rendition(31) # red foreground
    assert screen.cursor.attrs.fg == "red"
    assert screen.cursor.attrs.bg == "black"

def test_reset_resets_colors():
    screen = Screen(2, 2)
    assert screen.buffer == [[screen.default_char, screen.default_char]] * 2

    screen.select_graphic_rendition(30)
    screen.select_graphic_rendition(40)
    assert screen.cursor.attrs.fg == "black"
    assert screen.cursor.attrs.bg == "black"

    screen.select_graphic_rendition(0)
    assert screen.cursor.attrs == screen.default_char

def test_multi_attribs():
    screen = Screen(2, 2)
    assert screen.buffer == [[screen.default_char, screen.default_char]] * 2
    screen.select_graphic_rendition(1)
    screen.select_graphic_rendition(3)

    assert screen.cursor.attrs.bold
    assert screen.cursor.attrs.italics

def test_attributes_reset():
    screen = Screen(2, 2)
    screen.set_mode(mo.LNM)
    assert screen.buffer == [[screen.default_char, screen.default_char]] * 2
    screen.select_graphic_rendition(1)
    screen.draw("f")
    screen.draw("o")
    screen.draw("o")
    assert screen.buffer == [
        [Char("f", bold=True), Char("o", bold=True)],
        [Char("o", bold=True), screen.default_char  ],
    ]

    screen.cursor_position()
    screen.select_graphic_rendition(0) # Reset
    screen.draw("f")
    assert screen.buffer == [
        [Char("f"),            Char("o", bold=True)],
        [Char("o", bold=True), screen.default_char  ],
    ]

def test_resize():
    screen = Screen(2, 2)
    screen.set_mode(mo.DECOM)
    screen.set_margins(0, 1)
    assert screen.size == (2, 2)
    assert screen.columns == 2
    assert len(screen.buffer[0]) == 2
    assert screen.buffer == [[screen.default_char, screen.default_char]] * 2

    screen.resize(3, 3)
    assert screen.size == (3, 3)
    assert len(screen.buffer) == 3
    assert len(screen.buffer[0]) == 3
    assert screen.buffer == [[screen.default_char,
                       screen.default_char,
                       screen.default_char]] * 3
    assert mo.DECOM not in screen.mode
    assert screen.margins == (0, 2)

    screen.resize(2, 2)
    assert screen.size == (2, 2)
    assert len(screen.buffer) == 2
    assert len(screen.buffer[0]) == 2
    assert screen.buffer == [[screen.default_char, screen.default_char]] * 2

    # quirks:
    # a) if the current display is thinner than the requested size,
    #    new columns should be added to the right.
    screen = update(Screen(2, 2), ["bo", "sh"], [None, None])
    screen.resize(2, 3)
    assert screen.display == ["bo ", "sh "]

    # b) if the current display is wider than the requested size,
    #    columns should be removed from the right...
    screen = update(Screen(2, 2), ["bo", "sh"], [None, None])
    screen.resize(2, 1)
    assert screen.display == ["b", "s"]

    # c) if the current display is shorter than the requested
    #    size, new rows should be added on the bottom.
    screen = update(Screen(2, 2), ["bo", "sh"], [None, None])
    screen.resize(3, 2)

    assert screen.display == ["bo", "sh", "  "]

    # d) if the current display is taller than the requested
    #    size, rows should be removed from the top.
    screen = update(Screen(2, 2), ["bo", "sh"], [None, None])
    screen.resize(1, 2)
    assert screen.display == ["sh"]

def test_draw():
    # ``DECAWM`` on (default).
    screen = Screen(3, 3)
    screen.set_mode(mo.LNM)
    assert mo.DECAWM in screen.mode

    for ch in "abc":
        screen.draw(ch)

    assert screen.display == ["abc", "   ", "   "]
    assert (screen.cursor.y, screen.cursor.x) == (0, 3)

    # ... one` more character -- now we got a linefeed!
    screen.draw("a")
    assert (screen.cursor.y, screen.cursor.x) == (1, 1)

    # ``DECAWM`` is off.
    screen = Screen(3, 3)
    screen.reset_mode(mo.DECAWM)

    for ch in "abc":
        screen.draw(ch)

    assert screen.display == ["abc", "   ", "   "]
    assert (screen.cursor.y, screen.cursor.x) == (0, 3)

    # No linefeed is issued on the end of the line ...
    screen.draw("a")
    assert screen.display == ["aba", "   ", "   "]
    assert (screen.cursor.y, screen.cursor.x) == (0, 3)

    # ``IRM`` mode is on, expecting new characters to move the old ones
    # instead of replacing them.
    screen.set_mode(mo.IRM)
    screen.cursor_position()
    screen.draw("x")
    assert screen.display == ["xab", "   ", "   "]

    screen.cursor_position()
    screen.draw("y")
    assert screen.display == ["yxa", "   ", "   "]

def test_carriage_return():
    screen = Screen(3, 3)
    screen.cursor.x = 2
    screen.carriage_return()

    assert screen.cursor.x == 0

def test_index():
    screen = update(Screen(2, 2), ["wo", "ot"], colored=[1])

    # a) indexing on a row that isn't the last should just move
    # the cursor down.
    screen.index()
    assert (screen.cursor.y, screen.cursor.x) == (1, 0)
    assert screen.buffer == [
        [Char("w"), Char("o")],
        [Char("o", fg="red"), Char("t", fg="red")]
    ]

    # b) indexing on the last row should push everything up and
    # create a new row at the bottom.
    screen.index()
    assert screen.cursor.y == 1
    assert screen.buffer == [
        [Char("o", fg="red"), Char("t", fg="red")],
        [screen.default_char, screen.default_char]
    ]

    # c) same with margins
    screen = update(Screen(2, 5), ["bo", "sh", "th", "er", "oh"],
                    colored=[1, 2])
    screen.set_margins(2, 4)
    screen.cursor.y = 3

    # ... go!
    screen.index()
    assert (screen.cursor.y, screen.cursor.x) == (3, 0)
    assert screen.display == ["bo", "th", "er", "  ", "oh"]
    assert screen.buffer == [
        [Char("b"), Char("o", "default")],
        [Char("t", "red"), Char("h", "red")],
        [Char("e"), Char("r")],
        [screen.default_char, screen.default_char],
        [Char("o"), Char("h")],
    ]

    # ... and again ...
    screen.index()
    assert (screen.cursor.y, screen.cursor.x) == (3, 0)
    assert screen.display == ["bo", "er", "  ", "  ", "oh"]
    assert screen.buffer == [
        [Char("b"), Char("o")],
        [Char("e"), Char("r")],
        [screen.default_char, screen.default_char],
        [screen.default_char, screen.default_char],
        [Char("o"), Char("h")],
    ]

    # ... and again ...
    screen.index()
    assert (screen.cursor.y, screen.cursor.x) == (3, 0)
    assert screen.display == ["bo", "  ", "  ", "  ", "oh"]
    assert screen.buffer == [
        [Char("b"), Char("o")],
        [screen.default_char, screen.default_char],
        [screen.default_char, screen.default_char],
        [screen.default_char, screen.default_char],
        [Char("o"), Char("h")],
    ]

    # look, nothing changes!
    screen.index()
    assert (screen.cursor.y, screen.cursor.x) == (3, 0)
    assert screen.display == ["bo", "  ", "  ", "  ", "oh"]
    assert screen.buffer == [
        [Char("b"), Char("o")],
        [screen.default_char, screen.default_char],
        [screen.default_char, screen.default_char],
        [screen.default_char, screen.default_char],
        [Char("o"), Char("h")],
    ]

def test_reverse_index():
    screen = update(Screen(2, 2), ["wo", "ot"], colored=[0])

    # a) reverse indexing on the first row should push rows down
    # and create a new row at the top.
    screen.reverse_index()
    assert (screen.cursor.y, screen.cursor.x) == (0, 0)
    assert screen.buffer == [
        [screen.default_char, screen.default_char],
        [Char("w", fg="red"), Char("o", fg="red")]
    ]

    # b) once again ...
    screen.reverse_index()
    assert (screen.cursor.y, screen.cursor.x) == (0, 0)
    assert screen.buffer == [
        [screen.default_char, screen.default_char],
        [screen.default_char, screen.default_char],
    ]

    # c) same with margins
    screen = update(Screen(2, 5), ["bo", "sh", "th", "er", "oh"],
                    colored=[2, 3])
    screen.set_margins(2, 4)
    screen.cursor.y = 1

    # ... go!
    screen.reverse_index()
    assert (screen.cursor.y, screen.cursor.x) == (1, 0)
    assert screen.display == ["bo", "  ", "sh", "th", "oh"]
    assert screen.buffer == [
        [Char("b"), Char("o")],
        [screen.default_char, screen.default_char],
        [Char("s"), Char("h")],
        [Char("t", fg="red"), Char("h", fg="red")],
        [Char("o"), Char("h")],
    ]

    # ... and again ...
    screen.reverse_index()
    assert (screen.cursor.y, screen.cursor.x) == (1, 0)
    assert screen.display == ["bo", "  ", "  ", "sh", "oh"]
    assert screen.buffer == [
        [Char("b"), Char("o")],
        [screen.default_char, screen.default_char],
        [screen.default_char, screen.default_char],
        [Char("s"), Char("h")],
        [Char("o"), Char("h")],
    ]

     # ... and again ...
    screen.reverse_index()
    assert (screen.cursor.y, screen.cursor.x) == (1, 0)
    assert screen.display == ["bo", "  ", "  ", "  ", "oh"]
    assert screen.buffer == [
        [Char("b"), Char("o")],
        [screen.default_char, screen.default_char],
        [screen.default_char, screen.default_char],
        [screen.default_char, screen.default_char],
        [Char("o"), Char("h")],
    ]

    # look, nothing changes!
    screen.reverse_index()
    assert (screen.cursor.y, screen.cursor.x) == (1, 0)
    assert screen.display == ["bo", "  ", "  ", "  ", "oh"]
    assert screen.buffer == [
        [Char("b"), Char("o")],
        [screen.default_char, screen.default_char],
        [screen.default_char, screen.default_char],
        [screen.default_char, screen.default_char],
        [Char("o"), Char("h")],
    ]

def test_linefeed():
    screen = update(Screen(2, 2), ["bo", "sh"], [None, None])
    screen.set_mode(mo.LNM)

    # a) LNM on by default (that's what `vttest` forces us to do).
    assert mo.LNM in screen.mode
    screen.cursor.x, screen.cursor.y = 1, 0
    screen.linefeed()
    assert (screen.cursor.y, screen.cursor.x) == (1, 0)

    # b) LNM off.
    screen.reset_mode(mo.LNM)
    screen.cursor.x, screen.cursor.y = 1, 0
    screen.linefeed()
    assert (screen.cursor.y, screen.cursor.x) == (1, 1)

def test_tabstops():
    screen = Screen(10, 10)

    # Making sure initial tabstops are in place ...
    assert screen.tabstops == set([7])

    # ... and clearing them.
    screen.clear_tab_stop(3)
    assert not screen.tabstops

    screen.cursor.x = 1
    screen.set_tab_stop()
    screen.cursor.x = 8
    screen.set_tab_stop()

    screen.cursor.x = 0
    screen.tab()
    assert screen.cursor.x == 1
    screen.tab()
    assert screen.cursor.x == 8
    screen.tab()
    assert screen.cursor.x == 9
    screen.tab()
    assert screen.cursor.x == 9

def test_clear_tabstops():
    screen = Screen(10, 10)
    screen.clear_tab_stop(3)

    # a) clear a tabstop at current cusor location
    screen.cursor.x = 1
    screen.set_tab_stop()
    screen.cursor.x = 5
    screen.set_tab_stop()
    screen.clear_tab_stop()

    assert screen.tabstops == set([1])

    screen.set_tab_stop()
    screen.clear_tab_stop(0)

    assert screen.tabstops == set([1])

    # b) all tabstops
    screen.set_tab_stop()
    screen.cursor.x = 9
    screen.set_tab_stop()
    screen.clear_tab_stop(3)

    assert not screen.tabstops

def test_backspace():
    screen = Screen(2, 2)
    assert screen.cursor.x == 0
    screen.backspace()
    assert screen.cursor.x == 0
    screen.cursor.x = 1
    screen.backspace()
    assert screen.cursor.x == 0

def test_save_cursor():
    # a) cursor position
    screen = Screen(10, 10)
    screen.save_cursor()
    screen.cursor.x, screen.cursor.y = 3, 5
    screen.save_cursor()
    screen.cursor.x, screen.cursor.y = 4, 4

    screen.restore_cursor()
    assert screen.cursor.x == 3
    assert screen.cursor.y == 5

    screen.restore_cursor()
    assert screen.cursor.x == 0
    assert screen.cursor.y == 0

    # b) modes
    screen = Screen(10, 10)
    screen.set_mode(mo.DECAWM, mo.DECOM)
    screen.save_cursor()

    screen.reset_mode(mo.DECAWM)

    screen.restore_cursor()
    assert mo.DECAWM in screen.mode
    assert mo.DECOM in screen.mode

    # c) attributes
    screen = Screen(10, 10)
    screen.select_graphic_rendition(4)
    screen.save_cursor()
    screen.select_graphic_rendition(24)

    assert screen.cursor.attrs == screen.default_char

    screen.restore_cursor()

    assert screen.cursor.attrs != screen.default_char
    assert screen.cursor.attrs == Char(" ", underscore=True)

def test_restore_cursor_with_none_saved():
    screen = Screen(10, 10)
    screen.set_mode(mo.DECOM)
    screen.cursor.x, screen.cursor.y = 5, 5
    screen.restore_cursor()

    assert (screen.cursor.y, screen.cursor.x) == (0, 0)
    assert mo.DECOM not in screen.mode

def test_restore_cursor_out_of_bounds():
    screen = Screen(10, 10)

    # a) origin mode off.
    screen.cursor_position(5, 5)
    screen.save_cursor()
    screen.resize(3, 3)
    screen.reset()
    screen.restore_cursor()

    assert (screen.cursor.y, screen.cursor.x) == (2, 2)

    # b) origin mode is on.
    screen.resize(10, 10)
    screen.cursor_position(8, 8)
    screen.save_cursor()
    screen.resize(5, 5)
    screen.reset()
    screen.set_mode(mo.DECOM)
    screen.set_margins(2, 3)
    screen.restore_cursor()

    assert (screen.cursor.y, screen.cursor.x) == (2, 4)

def test_insert_lines():
    # a) without margins
    screen = update(Screen(3, 3), ["sam", "is ", "foo"], colored=[1])
    screen.insert_lines()

    assert (screen.cursor.y, screen.cursor.x) == (0, 0)
    assert screen.display == ["   ", "sam", "is "]
    assert screen.buffer == [
        [screen.default_char] * 3,
        [Char("s"), Char("a"), Char("m")],
        [Char("i", fg="red"), Char("s", fg="red"), Char(" ", fg="red")],
    ]

    screen = update(Screen(3, 3), ["sam", "is ", "foo"], colored=[1])
    screen.insert_lines(2)

    assert (screen.cursor.y, screen.cursor.x) == (0, 0)
    assert screen.display == ["   ", "   ", "sam"]
    assert screen.buffer == [
        [screen.default_char] * 3,
        [screen.default_char] * 3,
        [Char("s"), Char("a"), Char("m")]
    ]

    # b) with margins
    screen = update(Screen(3, 5), ["sam", "is ", "foo", "bar", "baz"],
                    colored=[2, 3])
    screen.set_margins(1, 4)
    screen.cursor.y = 1
    screen.insert_lines(1)

    assert (screen.cursor.y, screen.cursor.x) == (1, 0)
    assert screen.display == ["sam", "   ", "is ", "foo", "baz"]
    assert screen.buffer == [
        [Char("s"), Char("a"), Char("m")],
        [screen.default_char] * 3,
        [Char("i"), Char("s"), Char(" ")],
        [Char("f", fg="red"), Char("o", fg="red"), Char("o", fg="red")],
        [Char("b"), Char("a"), Char("z")],
    ]

    screen = update(Screen(3, 5), ["sam", "is ", "foo", "bar", "baz"],
                    colored=[2, 3])
    screen.set_margins(1, 3)
    screen.cursor.y = 1
    screen.insert_lines(1)

    assert (screen.cursor.y, screen.cursor.x) == (1, 0)
    assert screen.display == ["sam", "   ", "is ", "bar",  "baz"]
    assert screen.buffer == [
        [Char("s"), Char("a"), Char("m")],
        [screen.default_char] * 3,
        [Char("i"), Char("s"), Char(" ")],
        [Char("b", fg="red"), Char("a", fg="red"), Char("r", fg="red")],
        [Char("b"), Char("a"), Char("z")],
    ]

    screen.insert_lines(2)
    assert (screen.cursor.y, screen.cursor.x) == (1, 0)
    assert screen.display == ["sam", "   ", "   ", "bar",  "baz"]
    assert screen.buffer == [
        [Char("s"), Char("a"), Char("m")],
        [screen.default_char] * 3,
        [screen.default_char] * 3,
        [Char("b", fg="red"), Char("a", fg="red"), Char("r", fg="red")],
        [Char("b"), Char("a"), Char("z")],
    ]

    # c) with margins -- trying to insert more than we have available
    screen = update(Screen(3, 5), ["sam", "is ", "foo", "bar", "baz"],
                    colored=[2, 3])
    screen.set_margins(2, 4)
    screen.cursor.y = 1
    screen.insert_lines(20)

    assert (screen.cursor.y, screen.cursor.x) == (1, 0)
    assert screen.display == ["sam", "   ", "   ", "   ", "baz"]
    assert screen.buffer == [
        [Char("s"), Char("a"), Char("m")],
        [screen.default_char] * 3,
        [screen.default_char] * 3,
        [screen.default_char] * 3,
        [Char("b"), Char("a"), Char("z")],
    ]

    # d) with margins -- trying to insert outside scroll boundaries;
    #    expecting nothing to change
    screen = update(Screen(3, 5), ["sam", "is ", "foo", "bar", "baz"],
                    colored=[2, 3])
    screen.set_margins(2, 4)
    screen.insert_lines(5)

    assert (screen.cursor.y, screen.cursor.x) == (0, 0)
    assert screen.display == ["sam", "is ", "foo", "bar", "baz"]
    assert screen.buffer == [
        [Char("s"), Char("a"), Char("m")],
        [Char("i"), Char("s"), Char(" ")],
        [Char("f", fg="red"), Char("o", fg="red"), Char("o", fg="red")],
        [Char("b", fg="red"), Char("a", fg="red"), Char("r", fg="red")],
        [Char("b"), Char("a"), Char("z")],
    ]

def test_delete_lines():
    # a) without margins
    screen = update(Screen(3, 3), ["sam", "is ", "foo"], colored=[1])
    screen.delete_lines()

    assert (screen.cursor.y, screen.cursor.x) == (0, 0)
    assert screen.display == ["is ", "foo", "   "]
    assert screen.buffer == [
        [Char("i", fg="red"), Char("s", fg="red"), Char(" ", fg="red")],
        [Char("f"), Char("o"), Char("o")],
        [screen.default_char] * 3,
    ]

    screen.delete_lines(0)

    assert (screen.cursor.y, screen.cursor.x) == (0, 0)
    assert screen.display == ["foo", "   ", "   "]
    assert screen.buffer == [
        [Char("f"), Char("o"), Char("o")],
        [screen.default_char] * 3,
        [screen.default_char] * 3,
    ]

    # b) with margins
    screen = update(Screen(3, 5), ["sam", "is ", "foo", "bar", "baz"],
                    colored=[2, 3])
    screen.set_margins(1, 4)
    screen.cursor.y = 1
    screen.delete_lines(1)

    assert (screen.cursor.y, screen.cursor.x) == (1, 0)
    assert screen.display == ["sam", "foo", "bar", "   ", "baz"]
    assert screen.buffer == [
        [Char("s"), Char("a"), Char("m")],
        [Char("f", fg="red"), Char("o", fg="red"), Char("o", fg="red")],
        [Char("b", fg="red"), Char("a", fg="red"), Char("r", fg="red")],
        [screen.default_char] * 3,
        [Char("b"), Char("a"), Char("z")],
    ]

    screen = update(Screen(3, 5), ["sam", "is ", "foo", "bar", "baz"],
                    colored=[2, 3])
    screen.set_margins(1, 4)
    screen.cursor.y = 1
    screen.delete_lines(2)

    assert (screen.cursor.y, screen.cursor.x) == (1, 0)
    assert screen.display == ["sam", "bar", "   ", "   ", "baz"]
    assert screen.buffer == [
        [Char("s"), Char("a"), Char("m")],
        [Char("b", fg="red"), Char("a", fg="red"), Char("r", fg="red")],
        [screen.default_char] * 3,
        [screen.default_char] * 3,
        [Char("b"), Char("a"), Char("z")],
    ]

    # c) with margins -- trying to delete  more than we have available
    screen = update(Screen(3, 5),
        ["sam", "is ", "foo", "bar", "baz"],
        [None,
         None,
         [("red", "default")] * 3,
         [("red", "default")] * 3,
         None])
    screen.set_margins(1, 4)
    screen.cursor.y = 1
    screen.delete_lines(5)

    assert (screen.cursor.y, screen.cursor.x) == (1, 0)
    assert screen.display == ["sam", "   ", "   ", "   ", "baz"]
    assert screen.buffer == [
        [Char("s"), Char("a"), Char("m")],
        [screen.default_char] * 3,
        [screen.default_char] * 3,
        [screen.default_char] * 3,
        [Char("b"), Char("a"), Char("z")],
    ]

    # d) with margins -- trying to delete outside scroll boundaries;
    #    expecting nothing to change
    screen = update(Screen(3, 5), ["sam", "is ", "foo", "bar", "baz"],
                    colored=[2, 3])
    screen.set_margins(2, 4)
    screen.cursor.y = 0
    screen.delete_lines(5)

    assert (screen.cursor.y, screen.cursor.x) == (0, 0)
    assert screen.display == ["sam", "is ", "foo", "bar", "baz"]
    assert screen.buffer == [
        [Char("s"), Char("a"), Char("m")],
        [Char("i"), Char("s"), Char(" ")],
        [Char("f", fg="red"), Char("o", fg="red"), Char("o", fg="red")],
        [Char("b", fg="red"), Char("a", fg="red"), Char("r", fg="red")],
        [Char("b"), Char("a"), Char("z")],
    ]

def test_insert_characters():
    screen = update(Screen(3, 3), ["sam", "is ", "foo"], colored=[0])

    # a) normal case
    cursor = copy.copy(screen.cursor)
    screen.insert_characters(2)
    assert (screen.cursor.y, screen.cursor.x) == (cursor.y, cursor.x)
    assert screen.buffer[0] == [
        screen.default_char,
        screen.default_char,
        Char("s", fg="red")
    ]

    # b) now inserting from the middle of the line
    screen.cursor.y, screen.cursor.x = 2, 1
    screen.insert_characters(1)
    assert screen.buffer[2] == [Char("f"), screen.default_char, Char("o")]

    # c) inserting more than we have
    screen.insert_characters(10)
    assert screen.buffer[2] == [Char("f"), screen.default_char, screen.default_char]

    # d) 0 is 1
    screen = update(Screen(3, 3), ["sam", "is ", "foo"], colored=[0])

    screen.cursor_position()
    screen.insert_characters()
    assert screen.buffer[0] == [
        screen.default_char,
        Char("s", fg="red"), Char("a", fg="red")
    ]

    screen = update(Screen(3, 3), ["sam", "is ", "foo"], colored=[0])
    screen.cursor_position()
    screen.insert_characters(1)
    assert screen.buffer[0] == [
        screen.default_char,
        Char("s", fg="red"), Char("a", fg="red")
    ]

def test_delete_characters():
    screen = update(Screen(3, 3), ["sam", "is ", "foo"], colored=[0])
    screen.delete_characters(2)
    assert (screen.cursor.y, screen.cursor.x) == (0, 0)
    assert screen.display == ["m  ", "is ", "foo"]
    assert screen.buffer[0] == [
        Char("m", fg="red"),
        screen.default_char, screen.default_char
    ]

    screen.cursor.y, screen.cursor.x = 2, 2
    screen.delete_characters()
    assert (screen.cursor.y, screen.cursor.x) == (2, 2)
    assert screen.display == ["m  ", "is ", "fo "]

    screen.cursor.y, screen.cursor.x = 1, 1
    screen.delete_characters(0)
    assert (screen.cursor.y, screen.cursor.x) == (1, 1)
    assert screen.display == ["m  ", "i  ", "fo "]

    # ! extreme cases.
    screen = update(Screen(5, 1), ["12345"], colored=[0])
    screen.cursor.x = 1
    screen.delete_characters(3)
    assert (screen.cursor.y, screen.cursor.x) == (0, 1)
    assert screen.display == ["15   "]
    assert screen.buffer[0] == [
        Char("1", fg="red"),
        Char("5", fg="red"),
        screen.default_char,
        screen.default_char,
        screen.default_char
    ]

    screen = update(Screen(5, 1), ["12345"], colored=[0])
    screen.cursor.x = 2
    screen.delete_characters(10)
    assert (screen.cursor.y, screen.cursor.x) == (0, 2)
    assert screen.display == ["12   "]
    assert screen.buffer[0] == [
        Char("1", fg="red"),
        Char("2", fg="red"),
        screen.default_char,
        screen.default_char,
        screen.default_char
    ]

    screen = update(Screen(5, 1), ["12345"], colored=[0])
    screen.delete_characters(4)
    assert (screen.cursor.y, screen.cursor.x) == (0, 0)
    assert screen.display == ["5    "]
    assert screen.buffer[0] == [
        Char("5", fg="red"),
        screen.default_char,
        screen.default_char,
        screen.default_char,
        screen.default_char
    ]

def test_erase_character():
    screen = update(Screen(3, 3), ["sam", "is ", "foo"], colored=[0])

    screen.erase_characters(2)
    assert (screen.cursor.y, screen.cursor.x) == (0, 0)
    assert screen.display == ["  m", "is ", "foo"]
    assert screen.buffer[0] == [
        screen.default_char,
        screen.default_char,
        Char("m", fg="red")
    ]

    screen.cursor.y, screen.cursor.x = 2, 2
    screen.erase_characters()
    assert (screen.cursor.y, screen.cursor.x) == (2, 2)
    assert screen.display == ["  m", "is ", "fo "]

    screen.cursor.y, screen.cursor.x = 1, 1
    screen.erase_characters(0)
    assert (screen.cursor.y, screen.cursor.x) == (1, 1)
    assert screen.display == ["  m", "i  ", "fo "]

    # ! extreme cases.
    screen = update(Screen(5, 1), ["12345"], colored=[0])
    screen.cursor.x = 1
    screen.erase_characters(3)
    assert (screen.cursor.y, screen.cursor.x) == (0, 1)
    assert screen.display == ["1   5"]
    assert screen.buffer[0] == [
        Char("1", fg="red"),
        screen.default_char,
        screen.default_char,
        screen.default_char,
        Char("5", "red")
    ]

    screen = update(Screen(5, 1), ["12345"], colored=[0])
    screen.cursor.x = 2
    screen.erase_characters(10)
    assert (screen.cursor.y, screen.cursor.x) == (0, 2)
    assert screen.display == ["12   "]
    assert screen.buffer[0] == [
        Char("1", fg="red"),
        Char("2", fg="red"),
        screen.default_char,
        screen.default_char,
        screen.default_char
    ]

    screen = update(Screen(5, 1), ["12345"], colored=[0])
    screen.erase_characters(4)
    assert (screen.cursor.y, screen.cursor.x) == (0, 0)
    assert screen.display == ["    5"]
    assert screen.buffer[0] == [
        screen.default_char,
        screen.default_char,
        screen.default_char,
        screen.default_char,
        Char("5", fg="red")
    ]

def test_erase_in_line():
    screen = update(Screen(5, 5),
        ["sam i",
         "s foo",
         "but a",
         "re yo",
         "u?   "], colored=[0])
    screen.cursor_position(1, 3)

    # a) erase from cursor to the end of line
    screen.erase_in_line(0)
    assert (screen.cursor.y, screen.cursor.x) == (0, 2)
    assert screen.display == ["sa   ",
                              "s foo",
                              "but a",
                              "re yo",
                              "u?   "]
    assert screen.buffer[0] == [
        Char("s", fg="red"),
        Char("a", fg="red"),
        screen.default_char,
        screen.default_char,
        screen.default_char
    ]

    # b) erase from the beginning of the line to the cursor
    screen = update(screen,
        ["sam i",
         "s foo",
         "but a",
         "re yo",
         "u?   "], colored=[0])
    screen.erase_in_line(1)
    assert (screen.cursor.y, screen.cursor.x) == (0, 2)
    assert screen.display == ["    i",
                              "s foo",
                              "but a",
                              "re yo",
                              "u?   "]
    assert screen.buffer[0] == [
        screen.default_char,
        screen.default_char,
        screen.default_char,
        Char(" ", fg="red"),
        Char("i", fg="red")
    ]

    # c) erase the entire line
    screen = update(screen,
        ["sam i",
         "s foo",
         "but a",
         "re yo",
         "u?   "], colored=[0])
    screen.erase_in_line(2)
    assert (screen.cursor.y, screen.cursor.x) == (0, 2)
    assert screen.display == ["     ",
                              "s foo",
                              "but a",
                              "re yo",
                              "u?   "]
    assert screen.buffer[0] == [screen.default_char] * 5

def test_erase_in_display():
    screen = update(Screen(5, 5),
        ["sam i",
         "s foo",
         "but a",
         "re yo",
         "u?   "], colored=[2, 3])
    screen.cursor_position(3, 3)

    # a) erase from cursor to the end of the display, including
    #    the cursor
    screen.erase_in_display(0)
    assert (screen.cursor.y, screen.cursor.x) == (2, 2)
    assert screen.display == ["sam i",
                              "s foo",
                              "bu   ",
                              "     ",
                              "     "]
    assert screen.buffer[2:] == [
        [Char("b", fg="red"),
         Char("u", fg="red"),
         screen.default_char,
         screen.default_char,
         screen.default_char],
        [screen.default_char] * 5,
        [screen.default_char] * 5
    ]

    # b) erase from the beginning of the display to the cursor,
    #    including it
    screen = update(screen,
        ["sam i",
         "s foo",
         "but a",
         "re yo",
         "u?   "], colored=[2, 3])
    screen.erase_in_display(1)
    assert (screen.cursor.y, screen.cursor.x) == (2, 2)
    assert screen.display == ["     ",
                              "     ",
                              "    a",
                              "re yo",
                              "u?   "]
    assert screen.buffer[:3] == [
        [screen.default_char] * 5,
        [screen.default_char] * 5,
        [screen.default_char,
         screen.default_char,
         screen.default_char,
         Char(" ", fg="red"),
         Char("a", fg="red")],
    ]

    # c) erase the while display
    screen.erase_in_display(2)
    assert (screen.cursor.y, screen.cursor.x) == (2, 2)
    assert screen.display == ["     ",
                              "     ",
                              "     ",
                              "     ",
                              "     "]
    assert screen.buffer == [[screen.default_char] * 5] * 5

def test_cursor_up():
    screen = Screen(10, 10)

    # Moving the cursor up at the top doesn't do anything
    screen.cursor_up(1)
    assert screen.cursor.y == 0

    screen.cursor.y = 1

    # Moving the cursor past the top moves it to the top
    screen.cursor_up(10)
    assert screen.cursor.y == 0

    screen.cursor.y = 5
    # Can move the cursor more than one up.
    screen.cursor_up(3)
    assert screen.cursor.y == 2

def test_cursor_down():
    screen = Screen(10, 10)

    # Moving the cursor down at the bottom doesn't do anything
    screen.cursor.y = 9
    screen.cursor_down(1)
    assert screen.cursor.y == 9

    screen.cursor.y = 8

    # Moving the cursor past the bottom moves it to the bottom
    screen.cursor_down(10)
    assert screen.cursor.y == 9

    screen.cursor.y = 5
    # Can move the cursor more than one down.
    screen.cursor_down(3)
    assert screen.cursor.y == 8

def test_cursor_back():
    screen = Screen(10, 10)

    # Moving the cursor left at the margin doesn't do anything
    screen.cursor.x = 0
    screen.cursor_back(1)
    assert screen.cursor.x == 0

    screen.cursor.x = 3

    # Moving the cursor past the left margin moves it to the left margin
    screen.cursor_back(10)
    assert screen.cursor.x == 0

    screen.cursor.x = 5
    # Can move the cursor more than one back.
    screen.cursor_back(3)
    assert screen.cursor.x == 2

def test_cursor_forward():
    screen = Screen(10, 10)

    # Moving the cursor right at the margin doesn't do anything
    screen.cursor.x = 9
    screen.cursor_forward(1)
    assert screen.cursor.x == 9

    # Moving the cursor past the right margin moves it to the right margin
    screen.cursor.x = 8
    screen.cursor_forward(10)
    assert screen.cursor.x == 9

    # Can move the cursor more than one forward.
    screen.cursor.x = 5
    screen.cursor_forward(3)
    assert screen.cursor.x == 8

def test_cursor_position():
    screen = Screen(10, 10)

    # a) testing that we expect 1-indexed values
    screen.cursor_position(5, 10)
    assert (screen.cursor.y, screen.cursor.x) == (4, 9)

    # b) but (0, 0) is also accepted and should be the same as (1, 1)
    screen.cursor_position(0, 10)
    assert (screen.cursor.y, screen.cursor.x) == (0, 9)

    # c) moving outside the margins constrains to within the screen
    #    bounds
    screen.cursor_position(100, 5)
    assert (screen.cursor.y, screen.cursor.x) == (9, 4)

    screen.cursor_position(5, 100)
    assert (screen.cursor.y, screen.cursor.x) == (4, 9)

    # d) DECOM on
    screen.set_margins(5, 9)
    screen.set_mode(mo.DECOM)
    screen.cursor_position()
    assert (screen.cursor.y, screen.cursor.x) == (4, 0)

    screen.cursor_position(2, 0)
    assert (screen.cursor.y, screen.cursor.x) == (5, 0)

    # Note that cursor position doesn't change.
    screen.cursor_position(10, 0)
    assert (screen.cursor.y, screen.cursor.x) == (5, 0)

def test_unicode():
    screen = Screen(4, 2)
    stream = Stream()
    stream.attach(screen)

    try:
        stream.feed("тест")
    except UnicodeDecodeError:
        pytest.fail("Check your code -- we do accept unicode.")

    assert screen.display == ["тест", "    "]

def test_alignment_display():
    screen = Screen(5, 5)
    screen.set_mode(mo.LNM)
    screen.draw("a")
    screen.linefeed()
    screen.linefeed()
    screen.draw("b")

    assert screen.display == ["a    ",
                              "     ",
                              "b    ",
                              "     ",
                              "     "]

    screen.alignment_display()

    assert screen.display == ["EEEEE",
                              "EEEEE",
                              "EEEEE",
                              "EEEEE",
                              "EEEEE"]

def test_set_margins():
    screen = Screen(10, 10)

    assert screen.margins == (0, 9)

    # a) ok-case
    screen.set_margins(1, 5)
    assert screen.margins == (0, 4)

    # b) one of the margins is out of bounds
    screen.set_margins(100, 10)
    assert screen.margins != (99, 9)
    assert screen.margins == (0, 4)

    # c) no margins provided
    screen.set_margins()
    assert screen.margins != (None, None)
    assert screen.margins == (0, 4)

def test_hide_cursor():
    screen = Screen(10, 10)

    # DECTCEM is set by default.
    assert mo.DECTCEM in screen.mode
    assert not screen.cursor.hidden

    # a) resetting DECTCEM hides the cursor.
    screen.reset_mode(mo.DECTCEM)
    assert screen.cursor.hidden

    # b) ... and it's back!
    screen.set_mode(mo.DECTCEM)
    assert not screen.cursor.hidden


text:
         Read these guidelines in
         English
         عربى
         中文
         Português
         русский
         Español

# Pautas de Contribución

Hola 👋!

freeCodeCamp.org es posible gracias a miles de voluntarios amables como tú. Nos emocionados por tus contribuciones y nos complace darte la bienvenida a bordo.

Cumplimos estrictamente nuestro ["Código de conducta"](https://www.freecodecamp.org/code-of-conduct). Tómate un momento para leerlo. No es muy largo.

Feliz contribución 🎉!

## Aquí hay algunas maneras divertidas en que puedes ayudar:

Puedes optar por contribuir a cualquier área de tu interés:

1. [Contribuir a este código de código fuente abierto.](#contribute-to-this-open-source-codebase)

    Ayúdanos a crear o editar [artículos de guía](https://www.freecodecamp.org/guide), [desafíos de codificación](https://www.freecodecamp.org/learn) o corregir errores en la plataforma de aprendizaje.

2. Ayuda a los campistas en nuestro [foro público](https://www.freecodecamp.org/forum/):

    - ❓ [Respondiendo a sus preguntas](https://www.freecodecamp.org/forum/?max_posts=1).
    - 💬 [Dando retroalimentación](https://www.freecodecamp.org/forum/c/project-feedback?max_posts=1) a sus proyectos.

3. Ayúdanos a agregar subtítulos en nuestro [canal de YouTube](https://www.youtube.com/channel/UC8butISFwT-Wl7EV0hUK0BQ/videos).

## Contribuye a esta base de código abierto

Tenemos una gran base de código abierto de los miles de [Desafíos de Codificación](https://www.freecodecamp.org/learn) y [Artículos de Guía](https://www.freecodecamp.org/guide). Estos desafíos y artículos conforman el contenido de aprendizaje disponible en la plataforma de aprendizaje de freeCodeCamp.org.

Puedes ayudarnos con:

- [📝 Investigar, escribir y actualizar nuestros artículos de guía.](#investiga-escribe-y-actualiza-nuestros-artículos-de-guía)

- [💻 Crear, actualizar y corregir errores en nuestros desafíos de codificación.](#crear-actualizar-y-corregir-errores-en-nuestros-desafíos-de-codificación)

- [🌐 Traducir artículos de guía y desafíos de codificación.](#traducir-artículos-de-guía-y-desafíos-de-codificación)

- [🛠 Ayúdenos a corregir errores en la plataforma de aprendizaje de freeCodeCamp.org.](#ayúdenos-a-corregir-errores-en-la-plataforma-de-aprendizaje-de-freeCodeCamp.org)

### Investiga, escribe y actualiza nuestros artículos de guía

**¿Qué son los artículos de Guía?**

Los artículos de guía te ayudan a comprender rápidamente un concepto de tecnología. Estas son explicaciones breves y sencillas que puedes leer antes de ir a recursos más profundos.

Puedes encontrar un [artículo de ejemplo sobre elementos HTML aquí](./client/src/pages/html/elements/index.md).

**¿Sobre qué puedo escribir un artículo?**

Agradecemos tu ayuda al escribir estos artículos. No tienes que ser un experto en un tema para escribir sobre él; toda esta Guía es de código abierto, por lo que incluso si cometes un error, otro colaborador lo corregirá.

Para obtener ayuda, busca un `esbozo de artículo` en nuestro [sitio web de la Guía](https://www.freecodecamp.org/guide), escribe el artículo y luego abre una solicitud para reemplazar el esbozo con tu artículo. Una solicitud o [pull request](https://help.github.com/articles/about-pull-requests/) es la forma en que sugerirán los cambios. Permite a otros conocer tus cambios, revisarlos y adóptalos.

Si no puedes encontrar un esbozo sobre el tema que deseas escribir, puedes abrir un PR (pull request) que crea el esbozo e incluye tu borrador del artículo.

**Si deseas trabajar en ello, sigue estas pautas:**

#### [Cómo trabajar en los artículos de Guía.](/docs/how-to-work-on-guide-articles.md)

### Crear, actualizar y corregir errores en nuestros desafíos de codificación

Todos nuestros desafíos de codificación son curados por la comunidad, aportando conocimiento experto de voluntarios como tú.

Puedes ayudar a expandirlos y hacer que su redacción sea más clara. Puedes actualizar las historias de usuario para explicar mejor el concepto e incluso eliminar las redundantes. Es posible también que desees refactorizar las pruebas que no pasan con un código válido para las historias de usuario.

**Si deseas trabajar en ello, sigue estas pautas:**

#### [Cómo trabajar en desafíos de codificación.](/docs/how-to-work-on-coding-challenges.md)

### Traducir artículos de guía y desafíos de codificación

Puedes ayudarnos a traducir nuestros artículos de Guía y desafíos de codificación para un idioma que hables. Actualmente tenemos versiones traducidas en:

- Chino (中文)
- Ruso (русский)
- Árabe (عربى)
- Español
- Portugués (Português)

La mayoría de estas versiones están traducidas automáticamente. Nos encantaría tu ayuda para mejorar la calidad de estas traducciones.

**Si deseas trabajar en ello, sigue estas pautas:**

**[POR HACER/TODO]** Estamos en el proceso de actualizar documentos para el flujo de trabajo de traducción. Siga [`#18313`](https://github.com/freeCodeCamp/freeCodeCamp/issues/18313) para ver el progreso.

### Ayúdanos a corregir errores en la plataforma de aprendizaje de freeCodeCamp.org

Nuestra plataforma de aprendizaje se ejecuta en una moderna pila de JavaScript. Tiene varios componentes, herramientas y bibliotecas, que incluyen pero no se limitan a Node.js, MongoDB, LoopBack, OAuth 2.0, React, Gatsby, Webpack y más.

En general,

- Tenemos un servidor de API basado en Node.js.
- Un conjunto de aplicaciones cliente basadas en React.
- Un script que usamos para evaluar nuestros proyectos Frontend.

Contribuir a esto requiere cierta comprensión de las API, la sintaxis de ES6 y mucha curiosidad.

Esencialmente, esperamos cierta familiaridad básica con algunas de las tecnologías citadas, herramientas y bibliotecas mencionadas anteriormente. Dicho esto, no es necesario que seas un experto en ello.

Siéntete libre de hacernos preguntas, en los hilos relacionados con problemas, y estaremos encantados de aclarar. En caso de duda, puede comunicarse con Mrugesh Mohapatra [`@raisedadead`](https://github.com/raisedadead) o Stuart Taylor[`@bouncey`](https://github.com/bouncey) desde nuestra equipo de desarrollo de la plataforma para ayudarte con esto.

**Si deseas trabajar en ello, siga estas pautas:**

#### [Cómo configurar freeCodeCamp localmente.](/docs/how-to-setup-freecodecamp-locally.md)

## Preguntas frecuentes

**¿Cómo puedo reportar un error, que no está a bordo?**

Si crees que has encontrado un error, primero lee el artículo ["Ayuda, he encontrado un error"](https://forum.freecodecamp.org/t/how-to-report-a-bug/19543) y sigue sus instrucciones.

Si estás seguro de que es un error nuevo, siga adelante y cree un nuevo problema o Issue en GitHub. Asegúrate de incluir la mayor cantidad de información posible para que podamos reproducir el error. Tenemos una plantilla de problemas predefinida para ayudarte a superar esto.

Ten en cuenta que cualquier problema que busques ayuda de codificación en un desafío se cerrará. El rastreador de problemas es estrictamente para los temas y discusiones relacionados con el código base. Deberías [buscar ayuda en el foro](https://www.freecodecamp.org/forum) antes de informar en caso de duda.

**¿Cómo puedo reportar un problema de seguridad?**

Por favor, no crees Issues de GitHub para problemas de seguridad. En su lugar, envíe un correo electrónico a `security@freecodecamp.org` y lo veremos de inmediato.

**Estoy atascado en algo que no está en esta documentación. ¿Cómo puedo obtener ayuda?**

Siéntete libre de pedir ayuda en:

- [La categoría "Colaboradores" en nuestro foro público](https://www.freecodecamp.org/forum/c/contributors).
- [La sala de chat de nuestros colaboradores públicos en Gitter](https://gitter.im/FreeCodeCamp/Contributors).

Nos emociona ayudarte a contribuir con cualquiera de los temas en los que le gustaría trabajar. Asegúrate de buscar tu consulta antes de publicar una nueva. Sé educado y paciente. Nuestros voluntarios y moderadores de la comunidad están siempre alrededor para guiarlo a través de sus consultas.

**Soy nuevo en GitHub y Open Source en general:**

Lee nuestra [Guía de cómo contribuir al Código Abierto](https://github.com/freeCodeCamp/how-to-contribute-to-open-source).

**¿Qué significan estas diferentes etiquetas, que están etiquetadas en los problemas?**

Nuestros moderadores de nuestra comunidad [evalúan](https://en.wikipedia.org/wiki/Software_bug#Bug_management) problemas y solicitudes según su prioridad, gravedad y otros factores. Puedes [encontrar un glosario completo de sus significados aquí](https://github.com/freecodecamp/freecodecamp/labels).

Debes revisar los problemas **`Help Wanted`** o **`first timers welcome`** para una búsqueda rápida de lo que está disponible para que trabajes. Estos están libres para todos y no necesita permisión antes de trabajar en ellos.

Si estos temas carecen de claridad sobre lo que debe hacerse, siéntete libre de hacer preguntas en los comentarios.

**Encontré un error tipográfico, ¿debo informar un problema antes de poder hacer un pull request?**

Para los errores tipográficos y otros cambios de redacción, puedes abrir directamente los pull request sin crear primero Issues. Los Issues son más bien para discutir problemas más grandes asociados con el código, o aspectos más estructurales del currículo.


text:
package org.slc.sli.dashboard.manager.component;

import java.util.Collection;

import org.slc.sli.dashboard.entity.Config;
import org.slc.sli.dashboard.entity.GenericEntity;
import org.slc.sli.dashboard.entity.ModelAndViewConfig;

/**
 * Factory responsible for assembly of the required data for each component according to
 * the component's configuration.
 * @author agrebneva
 *
 */
public interface CustomizationAssemblyFactory {

    /**
     * Get required data and display metadata for a component
     * @param componentId
     * @param params
     * @return view config
     */
    ModelAndViewConfig getModelAndViewConfig(String componentId, Object entityKey);

    /**
     * Get data for the declared entity reference
     * @param componentId - component to get data for
     * @param entityKey - entity key for the component
     * @return entity
     */
    GenericEntity getDataComponent(String componentId, Object entityKey);

    /**
     * Get data for the declared entity reference overriding lazy
     * @param componentId - component to get data for
     * @param entityKey - entity key for the component
     * @param lazyOverride - override lazy?
     * @return view config
     */
    ModelAndViewConfig getModelAndViewConfig(String componentId, Object entityKey, boolean lazyOverride);

    /**
     * Get widget configs
     * @return collection of configs
     */
    Collection<Config> getWidgetConfigs();

}


text:
package libs

import (
	"encoding/json"
	"fmt"
	"os"
)

type Config struct {
	Env    string `json:"env"`
	Listen struct {
		HTTP string `json:"http"`
	} `json:"listen"`
	Github struct {
		Token    string `json:"token"`
		Username string `json:"username"`
	} `json:"github"`
	Template struct {
		Path string `json:"path"`
	} `json:"template"`
}

func NewConfig() *Config {
	c := &Config{}
	return c.loadEnvConfig()
}

func NewConfigFile(path string) *Config {
	c := NewConfig()
	file, _ := os.Open(path)
	decoder := json.NewDecoder(file)

	err := decoder.Decode(c)
	if err != nil {
		panic(fmt.Sprintf("Error parsing JSON config file: %s", err))
	}

	return c.loadEnvConfig()
}

func (c *Config) loadEnvConfig() *Config {
	if env := os.Getenv("ENV_NAME"); env != "" {
		c.Env = env
	}
	if env := os.Getenv("GITHUB_TOKEN"); env != "" {
		c.Github.Token = env
	}
	return c
}


text:
// Code generated by client-gen. DO NOT EDIT.

package v1

import (
	"context"
	"time"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	types "k8s.io/apimachinery/pkg/types"
	watch "k8s.io/apimachinery/pkg/watch"
	scheme "k8s.io/client-go/kubernetes/scheme"
	rest "k8s.io/client-go/rest"
)

// ComponentStatusesGetter has a method to return a ComponentStatusInterface.
// A group's client should implement this interface.
type ComponentStatusesGetter interface {
	ComponentStatuses() ComponentStatusInterface
}

// ComponentStatusInterface has methods to work with ComponentStatus resources.
type ComponentStatusInterface interface {
	Create(ctx context.Context, componentStatus *v1.ComponentStatus, opts metav1.CreateOptions) (*v1.ComponentStatus, error)
	Update(ctx context.Context, componentStatus *v1.ComponentStatus, opts metav1.UpdateOptions) (*v1.ComponentStatus, error)
	Delete(ctx context.Context, name string, opts *metav1.DeleteOptions) error
	DeleteCollection(ctx context.Context, opts *metav1.DeleteOptions, listOpts metav1.ListOptions) error
	Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.ComponentStatus, error)
	List(ctx context.Context, opts metav1.ListOptions) (*v1.ComponentStatusList, error)
	Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error)
	Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.ComponentStatus, err error)
	ComponentStatusExpansion
}

// componentStatuses implements ComponentStatusInterface
type componentStatuses struct {
	client rest.Interface
}

// newComponentStatuses returns a ComponentStatuses
func newComponentStatuses(c *CoreV1Client) *componentStatuses {
	return &componentStatuses{
		client: c.RESTClient(),
	}
}

// Get takes name of the componentStatus, and returns the corresponding componentStatus object, and an error if there is any.
func (c *componentStatuses) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.ComponentStatus, err error) {
	result = &v1.ComponentStatus{}
	err = c.client.Get().
		Resource("componentstatuses").
		Name(name).
		VersionedParams(&options, scheme.ParameterCodec).
		Do(ctx).
		Into(result)
	return
}

// List takes label and field selectors, and returns the list of ComponentStatuses that match those selectors.
func (c *componentStatuses) List(ctx context.Context, opts metav1.ListOptions) (result *v1.ComponentStatusList, err error) {
	var timeout time.Duration
	if opts.TimeoutSeconds != nil {
		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
	}
	result = &v1.ComponentStatusList{}
	err = c.client.Get().
		Resource("componentstatuses").
		VersionedParams(&opts, scheme.ParameterCodec).
		Timeout(timeout).
		Do(ctx).
		Into(result)
	return
}

// Watch returns a watch.Interface that watches the requested componentStatuses.
func (c *componentStatuses) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
	var timeout time.Duration
	if opts.TimeoutSeconds != nil {
		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
	}
	opts.Watch = true
	return c.client.Get().
		Resource("componentstatuses").
		VersionedParams(&opts, scheme.ParameterCodec).
		Timeout(timeout).
		Watch(ctx)
}

// Create takes the representation of a componentStatus and creates it.  Returns the server's representation of the componentStatus, and an error, if there is any.
func (c *componentStatuses) Create(ctx context.Context, componentStatus *v1.ComponentStatus, opts metav1.CreateOptions) (result *v1.ComponentStatus, err error) {
	result = &v1.ComponentStatus{}
	err = c.client.Post().
		Resource("componentstatuses").
		VersionedParams(&opts, scheme.ParameterCodec).
		Body(componentStatus).
		Do(ctx).
		Into(result)
	return
}

// Update takes the representation of a componentStatus and updates it. Returns the server's representation of the componentStatus, and an error, if there is any.
func (c *componentStatuses) Update(ctx context.Context, componentStatus *v1.ComponentStatus, opts metav1.UpdateOptions) (result *v1.ComponentStatus, err error) {
	result = &v1.ComponentStatus{}
	err = c.client.Put().
		Resource("componentstatuses").
		Name(componentStatus.Name).
		VersionedParams(&opts, scheme.ParameterCodec).
		Body(componentStatus).
		Do(ctx).
		Into(result)
	return
}

// Delete takes name of the componentStatus and deletes it. Returns an error if one occurs.
func (c *componentStatuses) Delete(ctx context.Context, name string, options *metav1.DeleteOptions) error {
	return c.client.Delete().
		Resource("componentstatuses").
		Name(name).
		Body(options).
		Do(ctx).
		Error()
}

// DeleteCollection deletes a collection of objects.
func (c *componentStatuses) DeleteCollection(ctx context.Context, options *metav1.DeleteOptions, listOptions metav1.ListOptions) error {
	var timeout time.Duration
	if listOptions.TimeoutSeconds != nil {
		timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second
	}
	return c.client.Delete().
		Resource("componentstatuses").
		VersionedParams(&listOptions, scheme.ParameterCodec).
		Timeout(timeout).
		Body(options).
		Do(ctx).
		Error()
}

// Patch applies the patch and returns the patched componentStatus.
func (c *componentStatuses) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.ComponentStatus, err error) {
	result = &v1.ComponentStatus{}
	err = c.client.Patch(pt).
		Resource("componentstatuses").
		Name(name).
		SubResource(subresources...).
		VersionedParams(&opts, scheme.ParameterCodec).
		Body(data).
		Do(ctx).
		Into(result)
	return
}


text:
<?xml version="1.0"?>
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
    <deployment>
        <dependencies/>
    </deployment>
</jboss-deployment-structure>


text:
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.models;

import com.fasterxml.jackson.annotation.JsonProperty;

/**
 * A complex configuration object that has multiple properties stored in Azure App Configuration service.
 */
public class ComplexConfiguration {
    @JsonProperty("endpointUri")
    private String endpointUri;

    @JsonProperty("name")
    private String name;

    @JsonProperty("numberOfInstances")
    private int numberOfInstances;

    /**
     * Gets the endpoint URI.
     * @return The endpoint URI.
     */
    public String endpointUri() {
        return endpointUri;
    }

    /**
     * Sets the endpoint URI.
     * @param endpointUri The endpoint URI for this ComplexConfiguration.
     * @return The updated object.
     */
    public ComplexConfiguration endpointUri(String endpointUri) {
        this.endpointUri = endpointUri;
        return this;
    }

    /**
     * Gets the name of the ComplexConfiguration.
     * @return The name of the complex object.
     */
    public String name() {
        return name;
    }

    /**
     * Sets the name of the ComplexConfiguration.
     * @param name The name to set for this ComplexConfiguration.
     * @return The updated object.
     */
    public ComplexConfiguration name(String name) {
        this.name = name;
        return this;
    }

    /**
     * Gets the number of instances.
     * @return The number of instances.
     */
    public int numberOfInstances() {
        return numberOfInstances;
    }

    /**
     * Sets the number of instances.
     * @param numberOfInstances The number of instances for this ComplexConfiguration.
     * @return The updated object.
     */
    public ComplexConfiguration numberOfInstances(int numberOfInstances) {
        this.numberOfInstances = numberOfInstances;
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return "Name: " + name() + ", Endpoint: " + endpointUri() + ", # of instances: " + numberOfInstances();
    }
}


text:
import numpy as np
import math
from copy import deepcopy
from numpy.linalg import norm, eig
import functools

def PrintStep(stepName):
    print("\n------------------------------\n{}\n------------------------------\n".format(stepName))

def ReadPrecision():
    return float(input())

def ReadMatrix():
    matrixSize = int(input())

    matrix = [list(map(float, input().split())) for i in range(matrixSize)]

    return matrix

def MakeHouseholder(column, iteration):
    v = np.zeros(len(column))

    v[iteration] = column[iteration] + np.sign(column[iteration]) * norm(column[iteration:])
    for i in range(iteration + 1, len(column)):
        v[i] = column[i]
    h = np.eye(len(column)) - (2 / (v @ v)) * (v[:, None] @ v[None, :])

    return h

def MakeQRDecomposition(matrix):
    PrintStep("QR Decomposition")
    qMatrix = np.eye(len(matrix))
    rMatrix = matrix.copy()

    for i in range(len(matrix) - 1):
        print("QR step number {}".format(i))
        house = MakeHouseholder(rMatrix[:, i], i)
        print("householder matrix:\n{}".format(house))
        qMatrix = qMatrix @ house
        print("Q matrix:\n{}".format(qMatrix))
        rMatrix = house @ rMatrix
        print("R matrix:\n{}".format(rMatrix))

    return qMatrix, rMatrix

def FindEigens(mtx, idx):
    matrix = mtx.tolist()
    a11 = matrix[idx][idx]
    a12 = matrix[idx][idx + 1]
    a21 = matrix[idx + 1][idx]
    a22 = matrix[idx + 1][idx + 1]
    print("my beatifur submatrix:\n{} {}\n{} {}".format(a11, a12, a21, a22))
    return np.roots((1, -a11 - a22, a11 * a22 - a12 * a21))

def Check(matrix, eps):
    PrintStep("Checking if we are close")
    result = []
    idx = 0
    while idx < len(matrix):
        if norm(matrix[idx + 1:, idx]) <= eps:
            print("{} is good".format(matrix.item(idx, idx)))
            result.append(matrix.item(idx, idx))

        elif norm(matrix[idx + 2:, idx]) <= eps:
            roots = FindEigens(matrix, idx)
            print("these roots are stable:\n{}".format(roots))
            result.extend(roots)
            idx += 1

        idx += 1

    print("got something in the check:\n{}".format(result))
    if len(result) + 1 == len(matrix):
        print("!!!!ALERT!!!!check didnt find all eigen values!")
        #result.append(matrix.item(-1, -1))

    return result

def FindEigenValues(matrix, prec, maxSteps = math.inf):
    PrintStep("Finding eigen values")

    currMatrix = deepcopy(matrix)
    result = []
    iterCount = 0
    isCheckPassed = False

    while True and iterCount != maxSteps:
        qMatrix, rMatrix = MakeQRDecomposition(currMatrix)

        PrintStep("After QR decomp")
        print("iteration = {}".format(iterCount))
        print("Q matrix:\n{}".format(qMatrix))
        print("R matrix:\n{}".format(rMatrix))

        currMatrix = rMatrix @ qMatrix
        print("Current matrix:\n{}".format(currMatrix))
        result = Check(currMatrix, prec)
        print("current eigen values:\n{}".format(result))

        if len(result) == len(matrix):
            if isCheckPassed == False:
                isCheckPassed = True
            else:
                break
        iterCount += 1

    return result, iterCount

if __name__ == "__main__":
    prec = ReadPrecision()
    matrix = np.matrix(ReadMatrix())

    eVal, iterations = FindEigenValues(matrix, prec)
    eigenValues = sorted(eVal)
    npEigens = sorted(eig(matrix)[0])
    PrintStep("Found all eigen values")
    print("wasted {} iterations with precision {}".format(iterations, prec))
    print("final eigen values:\n{}".format(eigenValues))
    print("numpy found these eigens:\n{}".format(npEigens))
    print("Are they close?\n{}".format(np.isclose(eigenValues, npEigens, prec, prec)))